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

History | View | Annotate | Download (38.4 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 java.util.logging.Level;
38
import org.apache.commons.lang3.StringUtils;
39

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

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

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

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

    
100
        protected GeometryManager geomManager = null;
101

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

    
105
        private String identifierQuoteString;
106

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

    
115
        }
116

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

    
134
        public static void execute(PreparedStatement st, String sql) throws SQLException {
135
            logger.debug("execute SQL: "+sql);
136
            st.execute();
137
        }
138
        
139
        public static int executeUpdate(PreparedStatement st) throws SQLException {
140
            return st.executeUpdate();
141
        }
142
        
143
        public void closeConnection(Connection connection) {
144
            this.getResource().closeConnection(connection);
145
        }
146
        
147
        public void execute(String sql) throws JDBCExecuteSQLException {
148
            try {
149
                Connection conn = this.getConnection();
150
                Statement st = conn.createStatement();
151
                JDBCHelper.execute(st, sql);            
152
                this.closeConnection(conn);
153
            } catch (Exception ex) {
154
               throw new JDBCExecuteSQLException(sql, ex);
155
            }
156
        }
157
        
158
        protected void initializeResource() throws InitializeException {
159
                ResourceManagerProviderServices manager = (ResourceManagerProviderServices) DALLocator
160
                                .getResourceManager();
161
                JDBCResource resource = (JDBCResource) manager
162
                                .createAddResource(
163
                                JDBCResource.NAME, new Object[] { params.getUrl(),
164
                                                params.getHost(), params.getPort(), params.getDBName(),
165
                                                params.getUser(), params.getPassword(),
166
                                                params.getJDBCDriverClassName() });
167
                this.setResource(resource);
168

    
169
        }
170

    
171
        protected final void setResource(JDBCResource resource) {
172
                this.resource = resource;
173
                this.resource.addConsumer(this);
174
        }
175

    
176
        public boolean closeResourceRequested(ResourceProvider resource) {
177
                return user.closeResourceRequested(resource);
178
        }
179

    
180
        public void resourceChanged(ResourceProvider resource) {
181
                user.resourceChanged(resource);
182

    
183
        }
184

    
185
        /**
186
         * open the resource
187
         *
188
         * @return true if the resourse was open in this call
189
         * @throws OpenException
190
         */
191
        public boolean open() throws OpenException {
192
                if (isOpen) {
193
                        return false;
194
                }
195
                // try {
196
                // begin();
197
                // } catch (ResourceExecuteException e1) {
198
                // throw new OpenException(name, e1);
199
                // }
200
                try {
201
                        getResource().execute(new ResourceAction() {
202
                                public Object run() throws Exception {
203
                                        getResource().connect();
204
                                        getResource().notifyOpen();
205

    
206
                                        user.opendDone();
207

    
208
                                        isOpen = true;
209
                                        return null;
210
                                }
211
                                public String toString() {
212
                                    return "open";
213
                                }
214
                        });
215
                        return true;
216
                } catch (ResourceExecuteException e) {
217
                        throw new OpenException(name, e);
218
                        // } finally {
219
                        // end();
220
                }
221

    
222
        }
223

    
224
        public JDBCResource getResource() {
225
                return resource;
226
        }
227

    
228
        public void close() throws CloseException {
229
                if (!isOpen) {
230
                        return;
231
                }
232
                // try {
233
                // begin();
234
                // } catch (ResourceExecuteException e) {
235
                // throw new CloseException(name, e);
236
                // }
237
                try {
238
                        getResource().execute(new ResourceAction() {
239
                                public Object run() throws Exception {
240
                                        isOpen = false;
241

    
242
                                        resource.notifyClose();
243
                                        user.closeDone();
244
                                        return null;
245
                                }
246
                        });
247
                } catch (ResourceExecuteException e) {
248
                        throw new CloseException(this.name, e);
249
                        // } finally {
250
                        // end();
251
                }
252
        }
253

    
254
        // public void end() {
255
        // resource.end();
256
        // }
257
        //
258
        // public void begin() throws ResourceExecuteException {
259
        // this.resource.begin();
260
        // }
261

    
262
        public Connection getConnection() throws AccessResourceException {
263
                return resource.getJDBCConnection();
264

    
265
        }
266

    
267
        @Override
268
        protected void doDispose() throws BaseException {
269
                this.close();
270
                resource.removeConsumer(this);
271
        }
272

    
273
        public boolean isOpen() {
274
                return isOpen;
275
        }
276

    
277
        /**
278
         * Executes an atomic action that uses an DB Connection.<br>
279
         *
280
         * This methos prepares a connection and close it at the end of execution of
281
         * action.<br>
282
         *
283
         * if <code>action</code> is an instance of {@link TransactionalAction} the
284
         * action will be execute inside of a DB transaction.
285
         *
286
         *
287
         * @param action
288
         * @throws Exception
289
         */
290
        public Object doConnectionAction(final ConnectionAction action)
291
                        throws Exception {
292
                this.open();
293
//                this.begin();
294
                return getResource().execute(new ResourceAction() {
295
                        public Object run() throws Exception {
296
                                Object result = null;
297
                                Connection conn = null;
298
                                boolean beginTrans = false;
299
                                try {
300
                                        conn = getConnection();
301
                                        if (action instanceof TransactionalAction) {
302
                                                // XXX OJO esta condicion NO ES FIABLE
303
                                                if (!conn.getAutoCommit()) {
304
                                                        if (!((TransactionalAction) action)
305
                                                                        .continueTransactionAllowed()) {
306
                                                                // FIXME exception
307
                                                                throw new Exception();
308
                                                        }
309
                                                }
310
                                                try {
311
                                                        conn.setAutoCommit(false);
312
                                                } catch (SQLException e) {
313
                                                        throw new JDBCSQLException(e);
314
                                                }
315
                                                beginTrans = true;
316
                                        }
317

    
318
                                        result = action.action(conn);
319

    
320
                                        if (beginTrans) {
321
                                                try {
322
                                                        conn.commit();
323
                                                } catch (SQLException e) {
324
                                                        throw new JDBCTransactionCommitException(e);
325
                                                }
326
                                        }
327

    
328
                                        return result;
329

    
330
                                } catch (Exception e) {
331

    
332
                                        if (beginTrans) {
333
                                                try {
334
                                                        conn.rollback();
335
                                                } catch (Exception e1) {
336
                                                        throw new JDBCTransactionRollbackException(e1, e);
337
                                                }
338
                                        }
339
                                        throw e;
340

    
341
                                } finally {
342
                                    closeConnection(conn);
343
                                }
344
                        }
345
                });
346

    
347
        }
348

    
349
        protected String getDefaultSchema(Connection conn) throws JDBCException {
350
                return defaultSchema;
351
        }
352

    
353
        protected EditableFeatureAttributeDescriptor createAttributeFromJDBC(
354
                        EditableFeatureType fType, Connection conn,
355
                        ResultSetMetaData rsMetadata, int colIndex)
356
        throws java.sql.SQLException {
357

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

    
421
                default:
422
                    column = createAttributeFromJDBCNativeType(fType, rsMetadata, colIndex);
423
                        break;
424
                }
425

    
426
                return column;
427

    
428
        }
429
        
430
        
431
        protected EditableFeatureAttributeDescriptor createAttributeFromJDBCNativeType(
432
        EditableFeatureType fType, ResultSetMetaData rsMetadata, int colIndex)
433
        throws SQLException {
434
        EditableFeatureAttributeDescriptor column;
435
        column = fType.add(rsMetadata.getColumnName(colIndex),
436
                DataTypes.OBJECT);
437
        column.setAdditionalInfo("SQLType", new Integer(rsMetadata
438
                .getColumnType(colIndex)));
439
        column.setAdditionalInfo("SQLTypeName", rsMetadata
440
                .getColumnTypeName(colIndex));
441
        return column;
442
    }
443
        
444

    
445
        protected EditableFeatureAttributeDescriptor getAttributeFromJDBC(
446
                        EditableFeatureType fType, Connection conn,
447
                        ResultSetMetaData rsMetadata, int colIndex) throws JDBCException {
448
                EditableFeatureAttributeDescriptor column;
449
                try {
450

    
451
                        column = createAttributeFromJDBC(fType, conn, rsMetadata, colIndex);
452
                        // column.setCaseSensitive(rsMetadata.isCaseSensitive(colIndex));
453
                        // column.setSqlType(rsMetadata.getColumnType(colIndex));
454
                        column.setAllowNull(
455
                                        rsMetadata.isNullable(colIndex) == ResultSetMetaData.columnNullable);
456
                        column.setIsAutomatic(rsMetadata.isAutoIncrement(colIndex));
457
                        column.setIsReadOnly(rsMetadata.isReadOnly(colIndex));
458
                        // column.setWritable(rsMetadata.isWritable(colIndex));
459
                        // column.setClassName(rsMetadata.getColumnClassName(colIndex));
460
                        // column.setCatalogName(rsMetadata.getCatalogName(colIndex));
461
                        // column.setDefinitelyWritable(rsMetadata
462
                        // .isDefinitelyWritable(colIndex));
463
                        // column.setLabel(rsMetadata.getColumnLabel(colIndex));
464
                        // column.setSchemaName(rsMetadata.getSchemaName(colIndex));
465
                        // column.setTableName(rsMetadata.getTableName(colIndex));
466
                        // column.setCatalogName(rsMetadata.getCatalogName(colIndex));
467
                        // column.setSqlTypeName();
468
                        // column.setSearchable(rsMetadata.isSearchable(colIndex));
469
                        // column.setSigned(rsMetadata.isSigned(colIndex));
470
                        // column.setCurrency(rsMetadata.isCurrency(colIndex));
471
                        column.setPrecision(rsMetadata.getPrecision(colIndex));
472
                        column.setScale(rsMetadata.getScale(colIndex));
473
                        column.setSize(rsMetadata.getColumnDisplaySize(colIndex));
474

    
475
                } catch (java.sql.SQLException e) {
476
                        throw new JDBCSQLException(e);
477
                }
478

    
479
                return column;
480

    
481
        }
482

    
483
        /**
484
         * Fill <code>featureType</code> geometry attributes with SRS and ShapeType
485
         * information
486
         *
487
         * <b>Override this if provider has native eometry support</b>
488
         *
489
         * @param conn
490
         * @param rsMetadata
491
         * @param featureType
492
         * @throws ReadException
493
         */
494
        protected void loadSRS_and_shapeType(Connection conn,
495
                        ResultSetMetaData rsMetadata, EditableFeatureType featureType,
496
                        String baseSchema, String baseTable) throws JDBCException {
497

    
498
                // Nothing to do
499

    
500
        }
501

    
502
        public void loadFeatureType(EditableFeatureType featureType,
503
                        JDBCStoreParameters storeParams) throws DataException {
504
                if (storeParams.getSQL() != null
505
                                && storeParams.getSQL().trim().length() > 0) {
506
                        loadFeatureType(featureType, storeParams, storeParams.getSQL(),
507
                                        null, null);
508
                } else {
509
                        String sql = "Select * from " + storeParams.tableID()
510
                                        + " where false";
511
                        loadFeatureType(featureType, storeParams, sql, storeParams
512
                                        .getSchema(), storeParams.getTable());
513
                }
514
        }
515

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

    
554
        protected String[] getPksFrom(Connection conn, JDBCStoreParameters params)
555
                throws JDBCException {
556
                try{
557
                        DatabaseMetaData metadata = conn.getMetaData();
558
                        ResultSet rsPrimaryKeys = null;
559
                        ResultSet rs = null;
560
                        String catalog = params.getCatalog();
561
                        String schema = params.getSchema();
562

    
563
                        try{
564
                                rs = metadata.getTables(catalog,
565
                                                schema, params.getTable(), null);
566

    
567
                                if (!rs.next()) {
568
                                        // No tables found with default values, ignoring catalog
569
                                        rs.close();
570
                                        catalog = null;
571
                                        schema = null;
572
                                        rs = metadata
573
                                                        .getTables(catalog, schema, params.getTable(), null);
574

    
575
                                        if (!rs.next()) {
576
                                                // table not found
577
                                                return null;
578
                                        } else if (rs.next()){
579
                                                // More that one, cant identify
580
                                                return null;
581
                                        }
582

    
583
                                } else if (rs.next()) {
584
                                        // More that one, cant identify
585
                                        return null;
586
                                }
587
                                rsPrimaryKeys = metadata.getPrimaryKeys(catalog, schema, params
588
                                                .getTable());
589
                                List pks = new ArrayList();
590
                                while (rsPrimaryKeys.next()){
591
                                        pks.add(rsPrimaryKeys.getString("COLUMN_NAME"));
592
                                }
593
                                return (String[]) pks.toArray(new String[pks.size()]);
594

    
595

    
596
                        } finally {
597
                                try{if (rs != null) {
598
                                        rs.close();
599
                                }} catch (SQLException ex) {logger.warn("Exception closing tables rs", ex);};
600
                                try{if (rsPrimaryKeys != null) {
601
                                        rsPrimaryKeys.close();
602
                                }} catch (SQLException ex) {logger.warn("Exception closing pk rs", ex);};
603
                        }
604

    
605

    
606
                } catch (SQLException e) {
607
                        logger.warn("Unable to get pk from DatabaseMetada", e);
608
                        return getPksFromInformationSchema(conn, params);
609
                }
610

    
611
        }
612

    
613
        protected String[] getPksFromInformationSchema(Connection conn,
614
                        JDBCStoreParameters params)
615
                        throws JDBCException {
616
                Statement st;
617
                StringBuffer sql = new StringBuffer();
618
                ResultSet rs;
619
                ArrayList list = new ArrayList();
620

    
621
                /*
622
                 select column_name as primary_key
623
                        from information_schema.table_constraints t_cons
624
                                inner join information_schema.key_column_usage c on
625
                                        c.constraint_catalog = t_cons.table_catalog and
626
                                    c.table_schema = t_cons.table_schema and
627
                                    c.table_name = t_cons.table_name and
628
                                        c.constraint_name = t_cons.constraint_name
629
                                where t_cons.table_schema = <schema>
630
                                and t_cons.constraint_catalog = <catalog>
631
                                 and t_cons.table_name = <table>
632
                                 and constraint_type = 'PRIMARY KEY'
633
                 */
634
                /*
635
                 * SELECT column_name FROM INFORMATION_SCHEMA.constraint_column_usage
636
                 * left join INFORMATION_SCHEMA.table_constraints on
637
                 * (INFORMATION_SCHEMA.table_constraints.constraint_name =
638
                 * INFORMATION_SCHEMA.constraint_column_usage.constraint_name and
639
                 * INFORMATION_SCHEMA.table_constraints.table_name =
640
                 * INFORMATION_SCHEMA.constraint_column_usage.table_name and
641
                 * INFORMATION_SCHEMA.table_constraints.table_schema =
642
                 * INFORMATION_SCHEMA.constraint_column_usage.table_schema) WHERE
643
                 * INFORMATION_SCHEMA.constraint_column_usage.table_name like
644
                 * 'muni10000_peq' AND
645
                 * INFORMATION_SCHEMA.constraint_column_usage.table_schema like 'public'
646
                 * AND INFORMATION_SCHEMA.constraint_column_usage.table_catalog like
647
                 * 'gis' AND constraint_type='PRIMARY KEY'
648
                 */
649

    
650
                sql.append("select column_name as primary_key ");
651
                sql.append("from information_schema.table_constraints t_cons ");
652
                sql.append("inner join information_schema.key_column_usage c on ");
653
                sql.append("c.constraint_catalog = t_cons.constraint_catalog and ");
654
                sql.append("c.table_schema = t_cons.table_schema and ");
655
                sql.append("c.table_name = t_cons.table_name and ");
656
                sql.append("c.constraint_name = t_cons.constraint_name ");
657
                sql.append("WHERE t_cons.table_name like '");
658

    
659
                sql.append(params.getTable());
660
                sql.append("' ");
661
                String schema = null;
662

    
663

    
664
                if (params.getSchema() == null || params.getSchema() == "") {
665
                        schema = getDefaultSchema(conn);
666
                } else {
667
                        schema = params.getSchema();
668
                }
669
                if (schema != null) {
670
                        sql.append(" and t_cons.table_schema like '");
671
                        sql.append(schema);
672
                        sql.append("' ");
673
                }
674

    
675
                if (params.getCatalog() != null && params.getCatalog() != "") {
676
                        sql
677
                                        .append(" and t_cons.constraint_catalog like '");
678
                        sql.append(params.getCatalog());
679
                        sql.append("' ");
680
                }
681

    
682
                sql.append("' and constraint_type = 'PRIMARY KEY'");
683

    
684
                // System.out.println(sql.toString());
685
                try {
686
                        st = conn.createStatement();
687
                        try {
688
                                rs = JDBCHelper.executeQuery(st, sql.toString());
689
                        } catch (java.sql.SQLException e) {
690
                                throw new JDBCExecuteSQLException(sql.toString(), e);
691
                        }
692
                        while (rs.next()) {
693
                                list.add(rs.getString(1));
694
                        }
695
                        rs.close();
696
                        st.close();
697

    
698
                } catch (java.sql.SQLException e) {
699
                        throw new JDBCSQLException(e);
700
                }
701
                if (list.size() == 0) {
702
                        return null;
703
                }
704

    
705
                return (String[]) list.toArray(new String[0]);
706

    
707
        }
708

    
709
    protected void loadFeatureType(Connection conn,
710
            EditableFeatureType featureType, String sql, String[] pks,
711
            String defGeomName, String schema, String table)
712
            throws DataException {
713

    
714
        Statement stAux = null;
715
        ResultSet rs = null;
716
        try {
717

    
718
            stAux = conn.createStatement();
719
            stAux.setFetchSize(1);
720

    
721
            try {
722
                rs = JDBCHelper.executeQuery(stAux, sql);
723
            } catch (SQLException e) {
724
                throw new JDBCExecuteSQLException(sql, e);
725
            }
726
            ResultSetMetaData rsMetadata = rs.getMetaData();
727

    
728
            List pksList = null;
729
            if (pks != null) {
730
                pksList = Arrays.asList(pks);
731

    
732
            }
733

    
734
            int i;
735
            int geometriesColumns = 0;
736
            String lastGeometry = null;
737

    
738
            EditableFeatureAttributeDescriptor attr;
739
            boolean firstGeometryAttrFound = false;
740
            for (i = 1; i <= rsMetadata.getColumnCount(); i++) {
741
                attr = getAttributeFromJDBC(featureType, conn, rsMetadata, i);
742
                if (pksList != null && pksList.contains(attr.getName())) {
743
                    attr.setIsPrimaryKey(true);
744
                }
745
                if (attr.getType() == DataTypes.GEOMETRY) {
746
                    geometriesColumns++;
747
                    lastGeometry = attr.getName();
748
                    // Set the default geometry attribute if it is the one
749
                    // given as parameter or it is the first one, just in case.
750
                    if (!firstGeometryAttrFound
751
                            || lastGeometry.equals(defGeomName)) {
752
                        firstGeometryAttrFound = true;
753
                        featureType
754
                                .setDefaultGeometryAttributeName(lastGeometry);
755
                    }
756
                }
757

    
758
            }
759

    
760
            if (geometriesColumns > 0) {
761
                loadSRS_and_shapeType(conn, rsMetadata, featureType, schema,
762
                        table);
763
            }
764

    
765
            if (defGeomName == null && geometriesColumns == 1) {
766
                featureType.setDefaultGeometryAttributeName(lastGeometry);
767
                defGeomName = lastGeometry;
768
            }
769

    
770
        } catch (java.sql.SQLException e) {
771
            throw new JDBCSQLException(e); // FIXME exception
772
        } finally {
773
            try {
774
                rs.close();
775
            } catch (Exception e) {
776
            }
777
            try {
778
                stAux.close();
779
            } catch (Exception e) {
780
            }
781

    
782
        }
783

    
784
    }
785

    
786
        /**
787
         * Override if provider has geometry support
788
         *
789
         * @param storeParams
790
         * @param geometryAttrName
791
         * @param limit
792
         * @return
793
         * @throws DataException
794
         */
795
        public Envelope getFullEnvelopeOfField(JDBCStoreParameters storeParams,
796
                        String geometryAttrName, Envelope limit) throws DataException {
797

    
798
                // TODO
799
                return null;
800

    
801
        }
802

    
803
        public Geometry getGeometry(byte[] buffer) throws BaseException {
804
                if (buffer == null) {
805
                        return null;
806
                }
807
                return geomManager.createFrom(buffer);
808
        }
809

    
810
        public String escapeFieldName(String field) {
811
                if (field.matches("[a-z][a-z0-9_]*")) {
812
                        return field;
813
                }
814
                String quote = getIdentifierQuoteString();
815
                return quote + field + quote;
816
        }
817

    
818
        public class DalValueToJDBCException extends WriteException {
819

    
820
                /**
821
                 * 
822
                 */
823
                private static final long serialVersionUID = -3608973505723097889L;
824
                private final static String MESSAGE_FORMAT = "Can't convert value of attribute '%(attributeName)' to JDBC type. %(problem) (attribute type '%(attributeType)', value class '%(valueClass)').";
825
                private final static String MESSAGE_KEY = "_Cant_convert_value_of_attribute_XattributeNameX_to_JDBC_type_XproblemX_attribute_type_XattributeTypeX_value_class_XvalueClassX";
826

    
827
                public DalValueToJDBCException(FeatureAttributeDescriptor attributeDescriptor, Object object, Throwable cause) {
828
                    this(attributeDescriptor, object, (String)null, cause);
829
                }
830
                
831
                public DalValueToJDBCException(FeatureAttributeDescriptor attributeDescriptor, Object object, String problem, Throwable cause) {
832
                        super(MESSAGE_FORMAT, cause, MESSAGE_KEY, serialVersionUID);
833
                        if( attributeDescriptor != null ) {
834
                                setValue("attributeName",attributeDescriptor.getName());
835
                                setValue("attributeType",attributeDescriptor.getDataTypeName());
836
                        } else {
837
                                setValue("attributeName","unknown");
838
                                setValue("attributeType","unknown");
839
                        }
840
                        if( object!=null ) {
841
                                setValue("valueClass", object.getClass().getName());
842
                        } else {
843
                                setValue("valueClass", "null");
844
                        }
845
                        if( problem!=null ) {
846
                            setValue("problem","");
847
                        } else {
848
                            setValue("problem",problem);
849
                        }
850
                }
851

    
852
        }
853

    
854
    protected Geometry coerce(GeometryType type, Geometry geometry) {
855
        try {
856
            GeometryType geomType = geometry.getGeometryType();
857
            if (geomType.isTypeOf(type)) {
858
                return geometry;
859
            }
860

    
861
            if (type.isTypeOf(Geometry.TYPES.MULTISURFACE)) {
862
                if (geomType.isTypeOf(Geometry.TYPES.SURFACE)) {
863
                    MultiPrimitive geom2 = (MultiPrimitive) geomManager.create(type.getType(), geomType.getSubType());
864
                    geom2.addPrimitive((Primitive) geometry);
865
                    return geom2;
866
                }
867
            } else if (type.isTypeOf(Geometry.TYPES.MULTICURVE)) {
868
                if (geomType.isTypeOf(Geometry.TYPES.CURVE)) {
869
                    MultiPrimitive geom2 = (MultiPrimitive) geomManager.create(type.getType(), geomType.getSubType());
870
                    geom2.addPrimitive((Primitive) geometry);
871
                    return geom2;
872
                }
873
            } else if (type.isTypeOf(Geometry.TYPES.MULTIPOINT)) {
874
                if (geomType.isTypeOf(Geometry.TYPES.POINT)) {
875
                    MultiPrimitive geom2 = (MultiPrimitive) geomManager.create(type.getType(), geomType.getSubType());
876
                    geom2.addPrimitive((Primitive) geometry);
877
                    return geom2;
878
                }
879
            }
880
        } catch (CreateGeometryException ex) {
881
            // Do nothing, return the same geometry.
882
            logger.trace("Can't coerce geometry '"+geometry+"' to '"+type+"'.",ex);
883
        }
884
        return geometry;
885
    }
886
        
887
    public Object dalValueToJDBC(
888
            FeatureAttributeDescriptor attributeDescriptor, Object value)
889
            throws WriteException {
890

    
891
        try {
892
            if (value == null) {
893
                return null;
894
            }
895
            Date jdate = null;
896
            switch (attributeDescriptor.getType()) {
897
                case DataTypes.DATE:
898
                    jdate = (Date)value;
899
                    java.sql.Date sqldate = new java.sql.Date(jdate.getTime());
900
                    return sqldate;
901
                    
902
                case DataTypes.TIME:
903
                    jdate = (Date)value;
904
                    java.sql.Time sqltime = new java.sql.Time(jdate.getTime());
905
                    return sqltime;
906

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

    
940
    }
941

    
942
        public String getSqlColumnTypeDescription(FeatureAttributeDescriptor attr) {
943
                switch (attr.getType()) {
944
                case DataTypes.STRING:
945
                        if (attr.getSize() < 1 || attr.getSize() > 255) {
946
                                return "text";
947
                        } else {
948
                                return "varchar(" + attr.getSize() + ")";
949
                        }
950
                case DataTypes.BOOLEAN:
951
                        return "bool";
952

    
953
                case DataTypes.BYTE:
954
                        return "smallint";
955

    
956
                case DataTypes.DATE:
957
                        return "date";
958

    
959
                case DataTypes.TIMESTAMP:
960
                        return "timestamp";
961

    
962
                case DataTypes.TIME:
963
                        return "time";
964

    
965
                case DataTypes.BYTEARRAY:
966
                case DataTypes.GEOMETRY:
967
                        return "blob";
968

    
969
                case DataTypes.DOUBLE:
970
//                        if (attr.getPrecision() > 0) {
971
//                            return "double precision(" + attr.getPrecision() + ')';
972
//                        } else {
973
                    //It works with PostgreSQL and MySQL. Check with others
974
                            return "double precision";
975
//                        }
976
                case DataTypes.FLOAT:
977
                        return "real";
978

    
979
                case DataTypes.INT:
980
                        if (attr.isAutomatic() && allowAutomaticValues()) {
981
                                return "serial";
982
                        } else {
983
                                return "integer";
984
                        }
985
                case DataTypes.LONG:
986
                        if (attr.isAutomatic()) {
987
                                return "bigserial";
988
                        } else {
989
                                return "bigint";
990
                        }
991

    
992
                default:
993
                        String typeName = (String) attr.getAdditionalInfo("SQLTypeName");
994
                        if (typeName != null) {
995
                                return typeName;
996
                        }
997

    
998
                        throw new UnsupportedDataTypeException(attr.getDataTypeName(), attr
999
                                        .getType());
1000
                }
1001
        }
1002

    
1003
        public int getProviderSRID(String srs) {
1004
                return -1;
1005
        }
1006

    
1007
        public int getProviderSRID(IProjection srs) {
1008
                return -1;
1009
        }
1010

    
1011
        public String getSqlFieldName(FeatureAttributeDescriptor attribute) {
1012
                return escapeFieldName(attribute.getName());
1013
        }
1014

    
1015
        public String getSqlFieldDescription(FeatureAttributeDescriptor attr)
1016
                        throws DataException {
1017

    
1018
                /**
1019
                 * column_name data_type [ DEFAULT default_expr ] [ column_constraint [
1020
                 * ... ] ]
1021
                 *
1022
                 * where column_constraint is:
1023
                 *
1024
                 * [ CONSTRAINT constraint_name ] { NOT NULL | NULL | UNIQUE | PRIMARY
1025
                 * KEY | CHECK (expression) | REFERENCES reftable [ ( refcolumn ) ] [
1026
                 * MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE action ] [ ON
1027
                 * UPDATE action ] } [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY
1028
                 * DEFERRED | INITIALLY IMMEDIATE ]
1029
                 */
1030

    
1031
                StringBuilder strb = new StringBuilder();
1032
                // name
1033
                strb.append(escapeFieldName(attr.getName()));
1034
                strb.append(" ");
1035

    
1036
                // Type
1037
                strb.append(this.getSqlColumnTypeDescription(attr));
1038
                strb.append(" ");
1039

    
1040
                boolean allowNull = attr.allowNull()
1041
                                && !(attr.isPrimaryKey() || attr.isAutomatic());
1042
                // Default
1043
                if (attr.getDefaultValue() == null) {
1044
                        if (allowNull) {
1045
                                strb.append("DEFAULT NULL ");
1046
                        }
1047
                } else {
1048
                        String value = getDefaltFieldValueString(attr);
1049
                        strb.append("DEFAULT '");
1050
                        strb.append(value);
1051
                        strb.append("' ");
1052
                }
1053

    
1054
                // Null
1055
                if (allowNull) {
1056
                        strb.append("NULL ");
1057
                } else {
1058
                        strb.append("NOT NULL ");
1059
                }
1060

    
1061
                // Primery key
1062
                if (attr.isPrimaryKey()) {
1063
                        strb.append("PRIMARY KEY ");
1064
                }
1065
                return strb.toString();
1066
        }
1067

    
1068
        /**
1069
         * @deprecated use getDefaultFieldValueString this has a type writer error.
1070
         */
1071
        protected String getDefaltFieldValueString(FeatureAttributeDescriptor attr)
1072
                        throws WriteException {
1073
                return getDefaultFieldValueString(attr);
1074
        }
1075

    
1076
        protected String getDefaultFieldValueString(FeatureAttributeDescriptor attr)
1077
                        throws WriteException {
1078
                return dalValueToJDBC(attr, attr.getDefaultValue()).toString();
1079
        }
1080

    
1081
        public String compoundLimitAndOffset(long limit, long offset) {
1082
                StringBuilder sql = new StringBuilder();
1083
                // limit
1084
                if (limit > 0) {
1085
                        sql.append(" limit ");
1086
                        sql.append(limit);
1087
                        sql.append(' ');
1088
                }
1089

    
1090
                // offset
1091
                if (offset > 0) {
1092
                        sql.append(" offset ");
1093
                        sql.append(offset);
1094
                        sql.append(' ');
1095
                }
1096
                return sql.toString();
1097
        }
1098

    
1099
        public boolean supportOffset() {
1100
                return true;
1101
        }
1102

    
1103
        public List getAdditionalSqlToCreate(NewDataStoreParameters ndsp,
1104
                        FeatureType fType) {
1105
                // TODO Auto-generated method stub
1106
                return null;
1107
        }
1108

    
1109

    
1110
        public String stringJoin(List listToJoin,String sep){
1111
                StringBuilder strb = new StringBuilder();
1112
                stringJoin(listToJoin,sep,strb);
1113
                return strb.toString();
1114
        }
1115

    
1116
        public void stringJoin(List listToJoin, String sep, StringBuilder strb) {
1117
                if (listToJoin.size() < 1) {
1118
                        return;
1119
                }
1120
                if (listToJoin.size() > 1) {
1121
                        for (int i = 0; i < listToJoin.size() - 1; i++) {
1122
                                strb.append(listToJoin.get(i));
1123
                                strb.append(sep);
1124
                        }
1125
                }
1126
                strb.append(listToJoin.get(listToJoin.size() - 1));
1127
        }
1128

    
1129
        /**
1130
         * Inform that provider has supports for geometry store and operations
1131
         * natively
1132
         *
1133
         * @return
1134
         */
1135
        protected boolean supportsGeometry() {
1136
                return false;
1137
        }
1138

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

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

    
1145
                                        ResultSet rs;
1146
                                        try {
1147
                                                DatabaseMetaData meta = conn.getMetaData();
1148
                                                rs = meta.getTypeInfo();
1149
                                                try{
1150
                                                        while (rs.next()) {
1151
                                                                if (rs.getInt("DATA_TYPE") == java.sql.Types.INTEGER) {
1152
                                                                        if (rs.getBoolean("AUTO_INCREMENT")) {
1153
                                                                                return Boolean.TRUE;
1154
                                                                        } else {
1155
                                                                                return Boolean.FALSE;
1156
                                                                        }
1157
                                                                }
1158
                                                        }
1159
                                                }finally{
1160
                                                        try{ rs.close();} catch (SQLException ex) {logger.error("Exception closing resulset", ex);};
1161
                                                }
1162
                                        } catch (SQLException e) {
1163
                                                throw new JDBCSQLException(e);
1164
                                        }
1165
                                        return Boolean.FALSE;
1166
                                }
1167

    
1168
                        };
1169

    
1170

    
1171

    
1172
                        try {
1173
                                allowAutomaticValues = (Boolean) doConnectionAction(action);
1174
                        } catch (Exception e) {
1175
                                logger.error("Exception checking for automatic integers", e);
1176
                                allowAutomaticValues = Boolean.FALSE;
1177
                        }
1178
                }
1179
                return allowAutomaticValues.booleanValue();
1180
        }
1181

    
1182
        public boolean supportsUnion() {
1183
                if (supportsUnions == null) {
1184
                        ConnectionAction action = new ConnectionAction() {
1185

    
1186
                                public Object action(Connection conn) throws DataException {
1187

    
1188
                                        try {
1189
                                                DatabaseMetaData meta = conn.getMetaData();
1190
                                                return new Boolean(meta.supportsUnion());
1191
                                        } catch (SQLException e) {
1192
                                                throw new JDBCSQLException(e);
1193
                                        }
1194
                                }
1195

    
1196
                        };
1197

    
1198
                        try {
1199
                                supportsUnions = (Boolean) doConnectionAction(action);
1200
                        } catch (Exception e) {
1201
                                logger.error("Exception checking for unions support", e);
1202
                                supportsUnions = Boolean.FALSE;
1203
                        }
1204
                }
1205
                return supportsUnions.booleanValue();
1206
        }
1207

    
1208
        protected String getIdentifierQuoteString() {
1209
                if (identifierQuoteString == null) {
1210
                ConnectionAction action = new ConnectionAction() {
1211

    
1212
                        public Object action(Connection conn) throws DataException {
1213

    
1214
                                try {
1215
                                        DatabaseMetaData meta = conn.getMetaData();
1216
                                        return meta.getIdentifierQuoteString();
1217
                                } catch (SQLException e) {
1218
                                        throw new JDBCSQLException(e);
1219
                                }
1220
                        }
1221

    
1222
                };
1223

    
1224
                try {
1225
                        identifierQuoteString = (String) doConnectionAction(action);
1226
                } catch (Exception e) {
1227
                        logger.error("Exception checking for unions support", e);
1228
                        identifierQuoteString = " ";
1229
                        }
1230
                }
1231
                return identifierQuoteString;
1232
        }
1233

    
1234
        protected boolean isReservedWord(String field) {
1235
                // TODO
1236
                return false;
1237
        }
1238

    
1239
        protected List createGrantStatements(JDBCNewStoreParameters ndsp) {
1240
            return this.createGrantStatements(ndsp, ndsp.tableID());
1241
        }
1242
        
1243
        protected List createGrantStatements(JDBCNewStoreParameters ndsp, String table) {
1244
            String priviligeParamNames[] = new String[] {   
1245
                "SelectRole",
1246
                "InsertRole",
1247
                "UpdateRole",
1248
                "DeleteRole",
1249
                "TruncateRole",
1250
                "ReferenceRole",
1251
                "TriggerRole",
1252
                "AllRole"
1253
            };
1254
            String priviligeNames[] = new String[] {   
1255
                "SELECT",
1256
                "INSERT",
1257
                "UPDATE",
1258
                "DELETE",
1259
                "TRUNCATE",
1260
                "REFERENCE",
1261
                "TRIGGER",
1262
                "ALL"
1263
            };
1264
            List statements = new ArrayList();;
1265
            
1266
            for( int i=0; i<priviligeParamNames.length; i++ ) {
1267
                String paramName = priviligeParamNames[i];
1268
                String roles = StringUtils.defaultIfBlank(
1269
                        (String) ndsp.getDynValue(paramName), 
1270
                        null
1271
                );
1272
                if( roles!=null ) {
1273
                    statements.addAll(this.createGrantStatements(table, priviligeNames[i], roles));
1274
                }
1275
            }
1276
            return statements;
1277
        }
1278

    
1279
        protected List createGrantStatements(String tableName, String privilege, String theRoles) {
1280
            List statements = new ArrayList();
1281
            String[] roles = StringUtils.split(theRoles,",");
1282
            for( int i=0; i<roles.length; i++) {
1283
                String statement = "GRANT "+ privilege + " ON TABLE " + tableName + " TO \"" + roles[i] + "\"";
1284
                statements.add(statement);
1285
            }
1286
            return statements;
1287
        } 
1288

    
1289
}