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.h2 / src / main / java / org / gvsig / fmap / dal / store / h2 / H2SpatialSQLBuilder.java @ 45425

History | View | Annotate | Download (24.2 KB)

1
package org.gvsig.fmap.dal.store.h2;
2

    
3
import java.sql.PreparedStatement;
4
import java.sql.SQLException;
5
import java.sql.Time;
6
import java.sql.Timestamp;
7
import java.text.MessageFormat;
8
import java.util.ArrayList;
9
import java.util.Date;
10
import java.util.List;
11
import java.util.Objects;
12
import org.apache.commons.lang3.tuple.Pair;
13
import org.gvsig.expressionevaluator.ExpressionBuilder.Parameter;
14
import org.gvsig.expressionevaluator.ExpressionUtils;
15
import org.gvsig.expressionevaluator.Formatter;
16
import org.gvsig.fmap.dal.DataTypes;
17
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
18
import org.gvsig.fmap.dal.feature.FeatureType;
19
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
20
import org.gvsig.fmap.dal.store.h2.expressionbuilderformatter.H2SpatialFormatter;
21
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase;
22
import org.gvsig.fmap.geom.Geometry;
23
import org.gvsig.fmap.geom.exception.CreateGeometryException;
24
import org.gvsig.tools.dataTypes.DataTypeUtils;
25
import org.gvsig.tools.dispose.Disposable;
26

    
27
public class H2SpatialSQLBuilder extends JDBCSQLBuilderBase {
28

    
29
    protected Formatter formatter = null;
30
    
31
    public H2SpatialSQLBuilder(H2SpatialHelper helper) {
32
        super(helper);
33
        
34
        //
35
        // H2/H2GIS SQL functions reference list
36
        //
37
        // http://www.h2database.com/html/functions.html
38
        // http://www.h2gis.org/docs/1.3/functions/
39
        //
40
        // http://www.h2database.com/html/grammar.html
41
        //
42
        // http://www.h2database.com/html/datatypes.html
43
        //
44
        //
45

    
46
        
47
        this.defaultSchema = "PUBLIC";
48
        this.supportSchemas = true;
49
        this.allowAutomaticValues = true;
50
        this.geometrySupportType = this.helper.getGeometrySupportType();
51
        this.hasSpatialFunctions = this.helper.hasSpatialFunctions();
52

    
53
        this.STMT_DELETE_GEOMETRY_COLUMN_FROM_TABLE_schema_table = null;
54
        this.STMT_DELETE_GEOMETRY_COLUMN_FROM_TABLE_table = null;
55
         
56
        this.STMT_UPDATE_TABLE_STATISTICS_table = "ANALYZE SAMPLE_SIZE 0";
57

    
58
        this.type_boolean = "BOOLEAN";
59
        this.type_byte = "TINYINT";
60
        this.type_bytearray = "BLOB";
61
        this.type_geometry = "GEOMETRY";
62
        this.type_char = "CHAR";
63
        this.type_date = "DATE";
64
        this.type_double = "DOUBLE"; 
65
        this.type_decimal_ps = "DECIMAL({0,Number,##########},{1,Number,##########})";
66
        this.type_decimal_p = "DECIMAL({0,Number,##########})";
67
        this.type_float = "REAL";
68
        this.type_int = "INTEGER";
69
        this.type_long = "BIGINT";
70
        this.type_string = "VARCHAR";
71
        this.type_string_p = "VARCHAR({0,Number,##########})";
72
        this.type_time = "TIME";
73
        this.type_timestamp = "TIMESTAMP";
74
        this.type_version = "VARCHAR";
75
        this.type_URI = "VARCHAR";
76
        this.type_URL = "VARCHAR";
77
        this.type_FILE = "VARCHAR";
78
        this.type_FOLDER = "VARCHAR";        
79
    }
80

    
81
    @Override
82
    protected Formatter formatter() {
83
        if( this.formatter==null ) {
84
            this.formatter = new H2SpatialFormatter(this);
85
        }
86
        return this.formatter;
87
    }
88

    
89
    public class H2SpatialTableNameBuilderBase extends TableNameBuilderBase {
90

    
91
        @Override
92
        public boolean has_database() {
93
            return false;
94
        }
95
        
96
    }
97

    
98
    public class H2SpatialCreateIndexBuilder extends CreateIndexBuilderBase {
99
    
100
        @Override
101
        public List<String> toStrings(Formatter formatter) {
102
            StringBuilder builder = new StringBuilder();
103
            builder.append("CREATE ");
104
            if( this.isUnique ) {
105
                builder.append("UNIQUE ");
106
            }
107
            if( this.isSpatial ) {
108
                builder.append("SPATIAL ");
109
            }
110
            builder.append("INDEX ");
111
            if( this.ifNotExist ) {
112
                builder.append("IF NOT EXISTS ");
113
            }
114
            builder.append(as_identifier(this.indexName));
115
            builder.append(" ON ");
116
            builder.append(this.table.toString(formatter));
117
            builder.append(" ( ");
118
            boolean is_first_column = true;
119
            for( String column : this.columns) {
120
                if( is_first_column ) {
121
                    is_first_column = false;
122
                } else {
123
                    builder.append(", ");
124
                }
125
                builder.append(as_identifier(column));
126
            }
127
            builder.append(" )");
128
            
129
            List<String> sqls = new ArrayList<>();
130
            sqls.add(builder.toString());
131
            return sqls;
132
        }
133

    
134
    }
135
        
136
    protected class H2SpatialAlterTableBuilderBase extends AlterTableBuilderBase {
137
        @Override
138
        public List<String> toStrings(Formatter formatter) {
139
            List<String> sqls = new ArrayList<>();
140
            if( this.isEmpty() ) {
141
                return sqls;
142
            }
143
            for (String column : drops) {
144
                StringBuilder builder = new StringBuilder();
145
                builder.append("ALTER TABLE ");
146
                builder.append(this.table.toString(formatter));
147
                builder.append(" DROP COLUMN IF EXISTS ");
148
                builder.append(as_identifier(column)); 
149
                sqls.add(builder.toString());
150
            }
151
            for (ColumnDescriptor column : adds) {
152
                StringBuilder builder = new StringBuilder();
153
                builder.append("ALTER TABLE ");
154
                builder.append(this.table.toString(formatter));
155
                builder.append(" ADD COLUMN ");
156
                builder.append(as_identifier(column.getName())); 
157
                builder.append(" ");
158
                if( column.getType() == DataTypes.INT && column.isAutomatic() ) {
159
                    builder.append(" SERIAL");
160
                } else {
161
                    builder.append(
162
                        sqltype(
163
                            column.getType(), 
164
                            column.getSize(),
165
                            column.getPrecision(), 
166
                            column.getScale(), 
167
                            column.getGeometryType(), 
168
                            column.getGeometrySubtype()
169
                        )
170
                    );
171
                }
172
                if (column.getDefaultValue() == null) {
173
                    if (column.allowNulls()) {
174
                        builder.append(" DEFAULT NULL");
175
                    }
176
                } else {
177
                    builder.append(" DEFAULT '");
178
                    builder.append(Objects.toString(column.getDefaultValue(),""));
179
                    builder.append("'");
180
                }
181
                if (column.allowNulls()) {
182
                    builder.append(" NULL");
183
                } else {
184
                    builder.append(" NOT NULL");
185
                }
186
                if (column.isPrimaryKey()) {
187
                    builder.append(" PRIMARY KEY");
188
                }
189
                sqls.add(builder.toString());
190
                
191
                if( column.isGeometry() ) {
192
                    String constraint_name = "constraint_" + this.table().getName() + "_" + column.getName()+"_geom";
193
                    String sql;
194
                    if( column.getGeometrySRSId()==null ) {
195
                      sql = MessageFormat.format(
196
                            "ALTER TABLE \"{0}\".\"{1}\" ADD CONSTRAINT IF NOT EXISTS \"{2}\" CHECK NVL2(\"{3}\", ST_GeometryTypeCode(\"{3}\") = {4,number,###} AND ST_CoordDim(\"{3}\") = {5,number,###}, TRUE)",
197
                            this.table().getSchema(),
198
                            this.table().getName(),
199
                            constraint_name,
200
                            column.getName(),
201
                            sqlgeometrytype(column.getGeometryType(),column.getGeometrySubtype()),
202
                            sqlgeometrynumdimension(column.getGeometryType(),column.getGeometrySubtype()),
203
                            column.getGeometrySRSId() 
204
                      );
205
                    } else {
206
                      sql = MessageFormat.format(
207
                            "ALTER TABLE \"{0}\".\"{1}\" ADD CONSTRAINT IF NOT EXISTS \"{2}\" CHECK NVL2(\"{3}\", ST_GeometryTypeCode(\"{3}\") = {4,number,###} AND ST_CoordDim(\"{3}\") = {5,number,###} AND ST_SRID(\"{3}\") = {6,number,#####}, TRUE)",
208
                            this.table().getSchema(),
209
                            this.table().getName(),
210
                            constraint_name,
211
                            column.getName(),
212
                            sqlgeometrytype(column.getGeometryType(),column.getGeometrySubtype()),
213
                            sqlgeometrynumdimension(column.getGeometryType(),column.getGeometrySubtype()),
214
                            column.getGeometrySRSId() 
215
                      );
216
                    }
217
                    sqls.add(sql);
218
                }
219
            }
220
            for (ColumnDescriptor column : alters) {
221
                StringBuilder builder = new StringBuilder();
222
                builder.append("ALTER TABLE ");
223
                builder.append(this.table.toString(formatter));
224
                builder.append(" ALTER COLUMN ");
225
                builder.append(as_identifier(column.getName())); 
226
                builder.append(" ");
227
                builder.append(
228
                    sqltype(
229
                        column.getType(), 
230
                        column.getSize(),
231
                        column.getPrecision(), 
232
                        column.getScale(), 
233
                        column.getGeometryType(), 
234
                        column.getGeometrySubtype()
235
                    )
236
                );
237
                if (column.getDefaultValue() == null) {
238
                    if (column.allowNulls()) {
239
                        builder.append(" DEFAULT NULL");
240
                    }
241
                } else {
242
                    builder.append(" DEFAULT '");
243
                    builder.append(column.getDefaultValue().toString());
244
                    builder.append("'");
245
                }
246
                if( column.isAutomatic() ) {
247
                    builder.append(" AUTO_INCREMENT");
248
                }
249
                sqls.add(builder.toString());
250
                if( column.isGeometry() ) {
251
                    String sql;
252
                    String constraint_name = "constraint_" + this.table().getName() + "_" + column.getName()+"_dim";
253
                    sql = MessageFormat.format(
254
                        "ALTER TABLE \"{0}\".\"{1}\" ADD CONSTRAINT IF NOT EXISTS \"{2}\" CHECK ST_CoordDim(\"{3}\") = {4}",
255
                        this.table().getSchema(),
256
                        this.table().getName(),
257
                        constraint_name,
258
                        column.getName(),
259
                        sqlgeometrynumdimension(column.getGeometryType(),column.getGeometrySubtype())
260
                    );
261
                    sqls.add(sql);
262
                }
263
            }
264
            for (Pair<String,String> pair : renames) {
265
                StringBuilder builder = new StringBuilder();
266
                builder.append("ALTER TABLE ");
267
                builder.append(this.table.toString(formatter));
268
                builder.append(" RENAME COLUMN ");
269
                builder.append(as_identifier(pair.getLeft())); 
270
                builder.append(" TO ");
271
                builder.append(as_identifier(pair.getRight())); 
272
                sqls.add(builder.toString());
273
            }
274
            return sqls;
275
        }
276

    
277
    }
278
    
279
    protected class H2SpatialCreateTableBuilder extends CreateTableBuilderBase {
280

    
281
        @Override
282
        public List<String> toStrings(Formatter formatter) {
283

    
284
            List<String> sqls = new ArrayList<>();
285
            StringBuilder builder = new StringBuilder();
286

    
287
            builder.append("CREATE TABLE ");
288
            builder.append(this.table.toString(formatter));
289
            builder.append(" (");
290
            boolean first = true;
291
            for (ColumnDescriptor column : columns) {
292

    
293
                if (first) {
294
                    first = false;
295
                } else {
296
                    builder.append(", ");
297
                }
298
                builder.append(as_identifier(column.getName()));
299
                builder.append(" ");
300
                builder.append(
301
                    sqltype(
302
                        column.getType(), 
303
                        column.getSize(),
304
                        column.getPrecision(), 
305
                        column.getScale(), 
306
                        column.getGeometryType(), 
307
                        column.getGeometrySubtype()
308
                    )
309
                );
310
                if( column.isGeometry() ) {
311
                    //
312
                    // https://github.com/orbisgis/h2gis/wiki/1.-Spatial-data#geometry-columns-view
313
                    // https://github.com/orbisgis/h2gis/blob/master/h2gis-functions/src/main/java/org/h2gis/functions/spatial/type/GeometryTypeFromConstraint.java
314
                    //
315
                    if( column.getGeometrySRSId()==null ) {
316
                      builder.append( 
317
                          MessageFormat.format(
318
                            " CHECK NVL2(\"{0}\", ST_GeometryTypeCode(\"{0}\") = {1} AND ST_CoordDim(\"{0}\") = {2}, TRUE)",
319
                            column.getName(),
320
                            sqlgeometrytype(column.getGeometryType(),column.getGeometrySubtype()),
321
                            sqlgeometrynumdimension(column.getGeometryType(),column.getGeometrySubtype())                        )
322
                      );
323
                    } else {
324
                      builder.append( 
325
                          MessageFormat.format(
326
                            " CHECK NVL2(\"{0}\", ST_GeometryTypeCode(\"{0}\") = {1,number,###} AND ST_CoordDim(\"{0}\") = {2,number,###} AND ST_SRID(\"{0}\") = {3,number,#####}, TRUE)",
327
                            column.getName(),
328
                            sqlgeometrytype(column.getGeometryType(),column.getGeometrySubtype()),
329
                            sqlgeometrynumdimension(column.getGeometryType(),column.getGeometrySubtype()),
330
                            column.getGeometrySRSId() 
331
                        )
332
                      );
333
                    }
334
                } else {
335
                    if (column.isPrimaryKey()) {
336
                        builder.append(" PRIMARY KEY");
337
                        if( column.isAutomatic() ) {
338
                            builder.append(" AUTO_INCREMENT");
339
                        }
340
                    } else {
341
                        if( column.isAutomatic() ) {
342
                            builder.append(" AUTO_INCREMENT");
343
                        }
344
                        if (column.getDefaultValue() == null || 
345
                                ExpressionUtils.isDynamicText(Objects.toString(column.getDefaultValue(), null))) {
346
                            if (column.allowNulls()) {
347
                                builder.append(" DEFAULT NULL");
348
                            }
349
                        } else {
350
                            switch(column.getType()) {
351
                                case DataTypes.TIMESTAMP:
352
                                    builder.append(" DEFAULT ( TIMESTAMP '");
353
                                    Timestamp dtimestamp = (Timestamp) DataTypeUtils.toTimestamp(column.getDefaultValue());
354
                                    builder.append(MessageFormat.format( "{0,date,yyyy-MM-dd HH:mm:ss.S}",dtimestamp));
355
                                    builder.append("' )");
356
                                    break;
357
                                case DataTypes.TIME:
358
                                    builder.append(" DEFAULT ( TIME '");
359
                                    Time dtime = (Time) DataTypeUtils.toTime(column.getDefaultValue());
360
                                    builder.append(MessageFormat.format( "{0,date,HH:mm:ss}",dtime));
361
                                    builder.append("' )");
362
                                    break;
363
                                case DataTypes.DATE:
364
                                    builder.append(" DEFAULT ( DATE '");
365
                                    java.sql.Date ddate = (java.sql.Date) DataTypeUtils.toDate(column.getDefaultValue());
366
                                    builder.append(MessageFormat.format( "{0,date,yyyy-MM-dd}",ddate));
367
                                    builder.append("' )");
368
                                    break;
369
                                default:
370
                                    builder.append(" DEFAULT '");
371
                                    builder.append(Objects.toString(column.getDefaultValue(),""));
372
                                    builder.append("'");
373
                            }
374
                        }
375
                    }
376
                }
377
                if (!column.allowNulls()) {
378
                    builder.append(" NOT NULL");
379
                }
380
            }
381
            builder.append(" )");
382
            sqls.add(builder.toString());
383
            return sqls;
384
        }
385
    }
386

    
387
    public class H2SpatialSelectBuilderBase extends SelectBuilderBase {
388
        
389
        @Override
390
        public String toString(Formatter formatter) {
391
            StringBuilder builder = new StringBuilder();
392
            if( !isValid(builder) ) {
393
                throw new IllegalStateException(builder.toString());
394
            }
395
            builder.append("SELECT ");
396
            if( this.distinct ) {
397
                builder.append("DISTINCT ");
398
            }
399
            boolean first = true;
400
            for (SelectColumnBuilder column : columns) {
401
                if (first) {
402
                    first = false;
403
                } else {
404
                    builder.append(", ");
405
                }
406
                builder.append(column.toString(formatter));
407
            }
408

    
409
            if ( this.has_from() ) {
410
                builder.append(" FROM ");
411
                builder.append(this.from.toString(formatter));
412
            }
413
            if ( this.has_where() ) {
414
                builder.append(" WHERE ");
415
                builder.append(this.where.toString(formatter));
416
            }
417
            if( this.has_group_by() ) {
418
                builder.append(" GROUP BY ");
419
                builder.append(this.groupColumn.get(0).toString(formatter));
420
                for (int i = 1; i < groupColumn.size(); i++) {
421
                    builder.append(", ");
422
                    builder.append(this.groupColumn.get(i).toString(formatter));
423
                }
424
            }
425
            if( this.has_order_by() ) {
426
                builder.append(" ORDER BY ");
427
                first = true;
428
                for (OrderByBuilder item : this.order_by) {
429
                    if (first) {
430
                        first = false;
431
                    } else {
432
                        builder.append(", ");
433
                    }
434
                    builder.append(item.toString(formatter));                    
435
                }   
436
            }
437
            
438
            if ( this.has_limit() && this.has_offset() ) {
439
                builder.append(" LIMIT ");
440
                builder.append(this.limit);
441
                builder.append(" OFFSET ");
442
                builder.append(this.offset);
443
                
444
            } else if ( this.has_limit()) {
445
                builder.append(" LIMIT ");
446
                builder.append(this.limit);
447

    
448
            } else if ( this.has_offset() ) {
449
                builder.append(" LIMIT -1 OFFSET ");
450
                builder.append(this.offset);    
451
            }
452
            return builder.toString();
453

    
454
        }
455
    }
456

    
457
    @Override
458
    public Object sqlgeometrydimension(int type, int subtype) {
459
        //'XY' or 2: 2D points, identified by X and Y coordinates
460
        //'XYM': 2D points, identified by X and Y coordinates. Each point stores an M-value (measure) as well
461
        //'XYZ' or 3: 3D points, identified by X, Y and Z coordinates
462
        //'XYZM' or 4: 3D points, identified by X, Y and Z coordinates. Each point stores an M-value (measure) as well
463
        switch(subtype) {
464
            case Geometry.SUBTYPES.GEOM2D:
465
            default:
466
                return "XY";
467
            case Geometry.SUBTYPES.GEOM2DM:
468
                return "XYM";
469
            case Geometry.SUBTYPES.GEOM3D:
470
                return "XYZ";
471
            case Geometry.SUBTYPES.GEOM3DM:
472
                return "XYZM";
473
        }
474
    }
475

    
476
    @Override
477
    public String sqltype(int type, int size, int precision, int scale, int geomtype, int geomSubtype) {
478
        if( type!=DataTypes.GEOMETRY ) {
479
            return super.sqltype(type, size, precision, scale, geomtype, geomSubtype);
480
        }
481
        return "GEOMETRY("+sqlgeometrytype(geomtype, geomSubtype)+")";
482
    }
483
    
484
    @Override
485
    public Object sqlgeometrytype(int geomtype, int geomsubtype) {
486
        //
487
        // https://github.com/orbisgis/h2gis/wiki/1.-Spatial-data#geometry-columns-view
488
        //
489
        switch(geomtype) {
490
        case Geometry.TYPES.POINT:
491
            return 1; 
492
        case Geometry.TYPES.MULTIPOINT:
493
            return 4; 
494
        case Geometry.TYPES.LINE:
495
            return 2; 
496
        case Geometry.TYPES.MULTILINE:
497
            return 5; 
498
        case Geometry.TYPES.POLYGON:
499
            return 3; 
500
        case Geometry.TYPES.MULTIPOLYGON:
501
            return 6;
502
        case Geometry.TYPES.GEOMETRY:
503
        default:
504
            return 0; // "GEOMETRY";
505
        }
506
    }
507
    
508
    public Object sqlgeometrynumdimension(int type, int subtype) {
509
        int dimensions=2;
510
        switch(subtype) {
511
        case Geometry.SUBTYPES.GEOM3D:
512
            dimensions = 3;
513
            break;
514
        case Geometry.SUBTYPES.GEOM2D:
515
            dimensions = 2;
516
            break;
517
        case Geometry.SUBTYPES.GEOM2DM:
518
            dimensions = 2; // ??????
519
            break;
520
        case Geometry.SUBTYPES.GEOM3DM:
521
            dimensions = 3; // ??????
522
            break;
523
        }
524
        return dimensions;
525
    }
526
    
527
    @Override
528
    public H2SpatialHelper getHelper() {
529
        return (H2SpatialHelper) this.helper;
530
    }
531
   
532
    @Override
533
    public Disposable setParameters(PreparedStatement st, FeatureProvider feature) {
534
        try {
535
            FeatureType featureType = feature.getType();
536
            List<Object> values = new ArrayList<>();
537
            for (Parameter parameter : this.parameters()) {
538
                if (parameter.is_constant()) {
539
                    values.add(parameter.value());
540
                } else {
541
                    String name = parameter.name();
542
                    FeatureAttributeDescriptor descriptor = featureType.getAttributeDescriptor(name);
543
                    switch( descriptor.getType() ) {
544
                    case DataTypes.DATE:
545
                        Date value = (Date)(feature.get(name));
546
                        if( value == null ) {
547
                            values.add(null);
548
                        } else {
549
                            values.add(new java.sql.Date(value.getTime()));
550
                        }
551
                        break;
552
                    case DataTypes.GEOMETRY:
553
                        Geometry geom = this.forceGeometryType(
554
                            descriptor.getGeomType(),
555
                            (Geometry)(feature.get(name))
556
                        );
557
                        values.add(geom);
558
                        break;
559
                    default:
560
                        values.add(feature.get(name));
561
                        break;
562
                    }
563
                }
564
            }
565
            return this.setStatementParameters(st, values, this.geometry_support_type());
566
        } catch (SQLException | CreateGeometryException ex) {
567
            String f = "unknow";
568
            try {
569
                f = feature.toString();
570
            } catch (Exception ex2) {
571
                // Do nothing
572
            }
573
            throw new RuntimeException("Can't set parameters to prepared statement from the feature (" + f + ")", ex);
574
        }
575
    }
576

    
577
    @Override
578
    protected AlterTableBuilder createAlterTableBuilder() {
579
        return new H2SpatialAlterTableBuilderBase();
580
    }
581
            
582
    @Override
583
    public TableNameBuilder createTableNameBuilder() {
584
        return new H2SpatialTableNameBuilderBase();
585
    }
586
    
587
    @Override
588
    protected CreateTableBuilder createCreateTableBuilder() {
589
        return new H2SpatialCreateTableBuilder();
590
    }
591

    
592
    @Override
593
    protected SelectBuilder createSelectBuilder() {
594
        return new H2SpatialSelectBuilderBase();
595
    }
596

    
597
    @Override
598
    protected CreateIndexBuilder createCreateIndexBuilder() {
599
        return new H2SpatialCreateIndexBuilder();
600
    }
601

    
602
}