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

History | View | Annotate | Download (22.1 KB)

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

    
3
import java.sql.PreparedStatement;
4
import java.text.MessageFormat;
5
import java.util.ArrayList;
6
import java.util.Date;
7
import java.util.List;
8
import org.apache.commons.lang3.tuple.Pair;
9
import org.gvsig.fmap.dal.DataTypes;
10
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
11
import org.gvsig.fmap.dal.feature.FeatureType;
12
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
13
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase;
14
import org.gvsig.fmap.geom.Geometry;
15
import org.gvsig.tools.dispose.Disposable;
16

    
17
public class H2SpatialSQLBuilder extends JDBCSQLBuilderBase {
18

    
19
    public H2SpatialSQLBuilder(H2SpatialHelper helper) {
20
        super(helper);
21
        
22
        //
23
        // H2/H2GIS SQL functions reference list
24
        //
25
        // http://www.h2database.com/html/functions.html
26
        // http://www.h2gis.org/docs/1.3/functions/
27
        //
28
        // http://www.h2database.com/html/grammar.html
29
        //
30
        // http://www.h2database.com/html/datatypes.html
31
        //
32
        //
33
        
34
        config.set(SQLConfig.default_schema, "");
35
        config.set(SQLConfig.support_schemas, false);
36
        config.set(SQLConfig.allowAutomaticValues, true);
37
        config.set(SQLConfig.geometry_type_support, this.helper.getGeometrySupportType());
38
        config.set(SQLConfig.has_spatial_functions, this.helper.hasSpatialFunctions());
39

    
40
        config.remove_functionality(SQLConfig.DELETE_GEOMETRY_COLUMN_FROM_TABLE_schema_table);
41
        config.remove_functionality(SQLConfig.DELETE_GEOMETRY_COLUMN_FROM_TABLE_table);
42
         
43
//        config.set(SQLConfig.UPDATE_TABLE_STATISTICS_table,"ANALYZE TABLE {0} SAMPLE_SIZE 0");
44
        config.set(SQLConfig.UPDATE_TABLE_STATISTICS_table,"ANALYZE SAMPLE_SIZE 0");
45

    
46
        config.set(SQLConfig.ST_GeomFromEWKB, "GeomFromWKB({0}, {1})");
47
        config.set(SQLConfig.ST_AsEWKB, "AsWKB(({0}))");        
48
        config.set(SQLConfig.ST_Intersects, "( (({0}) && ({1})) AND ST_Intersects(({0}),({1}) ))");  
49
        config.set(SQLConfig.ST_AsBinary, "NVL2({0}, ST_AsBinary({0}), null)");
50
        
51
        config.set(SQLConfig.lcase, "LOWER({0})");
52
        config.set(SQLConfig.ucase, "UPPER({0})");
53
        config.set(SQLConfig.operator_ILIKE, "({0}) ILIKE ({1})");
54
        config.set(SQLConfig.isNull, "( ({0}) IS NULL )");
55
        config.set(SQLConfig.notIsNull, "( ({0}) IS NOT NULL )");
56

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

    
81
    public class H2SpatialTableNameBuilderBase extends TableNameBuilderBase {
82

    
83
        @Override
84
        public boolean has_schema() {
85
            return false;
86
        }
87

    
88
        @Override
89
        public boolean has_database() {
90
            return false;
91
        }
92
        
93
    }
94

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

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

    
255
    }
256
    
257
    protected class H2SpatialCreateTableBuilder extends CreateTableBuilderBase {
258

    
259
        @Override
260
        public List<String> toStrings() {
261

    
262
            List<String> sqls = new ArrayList<>();
263
            StringBuilder builder = new StringBuilder();
264

    
265
            builder.append("CREATE TABLE ");
266
            builder.append(this.table.toString());
267
            builder.append(" (");
268
            boolean first = true;
269
            for (ColumnDescriptor column : columns) {
270

    
271
                if (first) {
272
                    first = false;
273
                } else {
274
                    builder.append(", ");
275
                }
276
                builder.append(identifier(column.getName()));
277
                builder.append(" ");
278
                builder.append(
279
                    sqltype(
280
                        column.getType(), 
281
                        column.getPrecision(), 
282
                        column.getSize(),
283
                        column.getGeometryType(), 
284
                        column.getGeometrySubtype()
285
                    )
286
                );
287
                if( !column.isGeometry() ) {
288
                    if (column.isPrimaryKey()) {
289
                        builder.append(" PRIMARY KEY");
290
                    }
291
                    if( column.isAutomatic() ) {
292
                        builder.append(" AUTO_INCREMENT");
293
                    }
294
                    if (column.getDefaultValue() == null) {
295
                        if (column.allowNulls()) {
296
                            builder.append(" DEFAULT NULL");
297
                        }
298
                    } else {
299
                        if( column.getType() == DataTypes.DATE ) {
300
                            builder.append(" DEFAULT ( TIMESTAMP '");
301
                            Date d = (Date) column.getDefaultValue();
302
                            builder.append(MessageFormat.format( "{0,date,yyyy-MM-dd HH:mm:ss.S}",d));
303
                            builder.append("' )");
304
                        } else {
305
                            builder.append(" DEFAULT '");
306
                            builder.append(column.getDefaultValue().toString());
307
                            builder.append("'");
308
                        }
309
                    }
310
                }
311
                if (!column.allowNulls()) {
312
                    builder.append(" NOT NULL");
313
                }
314
            }
315
            builder.append(" )");
316
            sqls.add(builder.toString());
317
            for (ColumnDescriptor column : columns) {
318
                if( column.isGeometry() ) {
319
                    String sql;
320
                    String constraint_name = "constraint_" + this.table().getName() + "_" + column.getName()+"_dim";
321
                    sql = MessageFormat.format(
322
                        "ALTER TABLE \"{0}\" ADD CONSTRAINT IF NOT EXISTS \"{1}\" CHECK ST_CoordDim(\"{2}\") = {3}",
323
                        this.table().getName(),
324
                        constraint_name,
325
                        column.getName(),
326
                        sqlgeometrynumdimension(column.getGeometryType(),column.getGeometrySubtype())
327
                    );
328
                    if( column.getGeometrySRSId()!=null ) {
329
                        String sql2;
330
                        String constraint_name2 = "constraint_" + this.table().getName() + "_" + column.getName()+"_srid";
331
                        sql2 = MessageFormat.format(
332
                            "ALTER TABLE \"{0}\" ADD CONSTRAINT IF NOT EXISTS \"{1}\" CHECK ST_SRID(\"{2}\") = {3,number,#####}",
333
                            this.table().getName(),
334
                            constraint_name2,
335
                            column.getName(),
336
                            column.getGeometrySRSId()
337
                        );
338
                        sqls.add(sql2);
339
                    }
340
                    sqls.add(sql);
341
                }
342
            }            
343
            
344
            return sqls;
345
        }
346
    }
347

    
348
    public class H2SpatialSelectBuilderBase extends SelectBuilderBase {
349
        
350
        @Override
351
        protected boolean isValid(StringBuilder message) {
352
            if( message == null ) {
353
                message = new StringBuilder();
354
            }
355
            if( this.has_offset() && !this.has_order_by() ) {
356
                // Algunos gestores de BBDD requieren que se especifique un
357
                // orden para poder usar OFFSET. Como eso parece buena idea para
358
                // asegurar que siempre tengamos los mismo resultados, lo exijimos
359
                // siempre.
360
                message.append("Can't use OFFSET without an ORDER BY.");
361
                return false;
362
            }
363
            return true;
364
        }        
365
        
366
        @Override
367
        public String toString() {
368
            StringBuilder builder = new StringBuilder();
369
            if( !isValid(builder) ) {
370
                throw new IllegalStateException(builder.toString());
371
            }
372
            builder.append("SELECT ");
373
            if( this.distinct ) {
374
                builder.append("DISTINCT ");
375
            }
376
            boolean first = true;
377
            for (SelectColumnBuilder column : columns) {
378
                if (first) {
379
                    first = false;
380
                } else {
381
                    builder.append(", ");
382
                }
383
                builder.append(column.toString());
384
            }
385

    
386
            if ( this.has_from() ) {
387
                builder.append(" FROM ");
388
                builder.append(this.from.toString());
389
            }
390
            if ( this.has_where() ) {
391
                builder.append(" WHERE ");
392
                builder.append(this.where.toString());
393
            }
394
            
395
            if( this.has_order_by() ) {
396
                builder.append(" ORDER BY ");
397
                first = true;
398
                for (OrderByBuilder item : this.order_by) {
399
                    if (first) {
400
                        first = false;
401
                    } else {
402
                        builder.append(", ");
403
                    }
404
                    builder.append(item.toString());                    
405
                }   
406
            }
407
            
408
            if ( this.has_limit() && this.has_offset() ) {
409
                builder.append(" LIMIT ");
410
                builder.append(this.limit);
411
                builder.append(" OFFSET ");
412
                builder.append(this.offset);
413
                
414
            } else if ( this.has_limit()) {
415
                builder.append(" LIMIT ");
416
                builder.append(this.limit);
417

    
418
            } else if ( this.has_offset() ) {
419
                builder.append(" LIMIT -1 OFFSET ");
420
                builder.append(this.offset);    
421
            }
422
            return builder.toString();
423

    
424
        }
425
    }
426

    
427
    @Override
428
    public String bytearray(byte[] data) {
429
        // H2Spatial usa un formato diferencte para especificar un array de 
430
        // bytes. En lugar de 0x... usa x'...' .
431
        StringBuilder builder = new StringBuilder();
432
        builder.append("x'");
433
        for (byte abyte : data) {
434
            int v = abyte & 0xff;
435
            builder.append(String.format("%02x", v));
436
        }
437
        builder.append("'");
438
        return builder.toString();
439
    }
440

    
441
    @Override
442
    public Object sqlgeometrydimension(int type, int subtype) {
443
        //'XY' or 2: 2D points, identified by X and Y coordinates
444
        //'XYM': 2D points, identified by X and Y coordinates. Each point stores an M-value (measure) as well
445
        //'XYZ' or 3: 3D points, identified by X, Y and Z coordinates
446
        //'XYZM' or 4: 3D points, identified by X, Y and Z coordinates. Each point stores an M-value (measure) as well
447
        switch(subtype) {
448
            case Geometry.SUBTYPES.GEOM2D:
449
            default:
450
                return "XY";
451
            case Geometry.SUBTYPES.GEOM2DM:
452
                return "XYM";
453
            case Geometry.SUBTYPES.GEOM3D:
454
                return "XYZ";
455
            case Geometry.SUBTYPES.GEOM3DM:
456
                return "XYZM";
457
        }
458
    }
459

    
460
    @Override
461
    public String sqltype(int type, int p, int s, int geomtype, int geomSubtype) {
462
        if( type!=DataTypes.GEOMETRY ) {
463
            return super.sqltype(type, p, s, geomtype, geomSubtype);
464
        }
465
        //
466
        // https://github.com/orbisgis/h2gis/wiki/1.-Spatial-data#geometry-columns-view
467
        //
468
        switch(geomtype) {
469
        case Geometry.TYPES.POINT:
470
            return "POINT";
471
        case Geometry.TYPES.MULTIPOINT:
472
            return "MULTIPOINT";
473
        case Geometry.TYPES.LINE:
474
            return "LINESTRING";
475
        case Geometry.TYPES.MULTILINE:
476
            return "MULTILINESTRING";
477
        case Geometry.TYPES.POLYGON:
478
            return "POLYGON";
479
        case Geometry.TYPES.MULTIPOLYGON:
480
            return "MULTIPOLYGON";
481
        default:
482
            return "GEOMETRY";
483
        }
484
    }
485
    
486
    public Object sqlgeometrynumdimension(int type, int subtype) {
487
        int dimensions=2;
488
        switch(subtype) {
489
        case Geometry.SUBTYPES.GEOM3D:
490
            dimensions = 3;
491
            break;
492
        case Geometry.SUBTYPES.GEOM2D:
493
            dimensions = 2;
494
            break;
495
        case Geometry.SUBTYPES.GEOM2DM:
496
            dimensions = 2; // ??????
497
            break;
498
        case Geometry.SUBTYPES.GEOM3DM:
499
            dimensions = 3; // ??????
500
            break;
501
        }
502
        return dimensions;
503
    }
504
    
505
    public H2SpatialHelper getHelper() {
506
        return (H2SpatialHelper) this.helper;
507
    }
508
   
509
    @Override
510
    public Disposable setParameters(PreparedStatement st, FeatureProvider feature) {
511
        try {
512
            FeatureType featureType = feature.getType();
513
            List<Object> values = new ArrayList<>();
514
            for (Parameter parameter : this.getParameters()) {
515
                if (parameter.is_constant()) {
516
                    values.add(parameter.getValue());
517
                } else {
518
                    String name = parameter.getName();
519
                    FeatureAttributeDescriptor descriptor = featureType.getAttributeDescriptor(name);
520
                    switch( descriptor.getType() ) {
521
                    case DataTypes.DATE:
522
                        Date value = (Date)(feature.get(name));
523
                        if( value == null ) {
524
                            values.add(null);
525
                        } else {
526
                            values.add(value.getTime());
527
                        }
528
                        break;
529
                    case DataTypes.GEOMETRY:
530
                        Geometry geom = this.forceGeometryType(
531
                            descriptor.getGeomType(),
532
                            (Geometry)(feature.get(name))
533
                        );
534
                        values.add(geom);
535
                        break;
536
                    default:
537
                        values.add(feature.get(name));
538
                        break;
539
                    }
540
                }
541
            }
542
            return this.setStatementParameters(st, values, this.geometry_support_type());
543
        } catch (Exception ex) {
544
            String f = "unknow";
545
            try {
546
                f = feature.toString();
547
            } catch (Exception ex2) {
548
                // Do nothing
549
            }
550
            throw new RuntimeException("Can't set parameters to prepared statement from the feature (" + f + ")", ex);
551
        }
552
    }
553

    
554
    @Override
555
    protected AlterTableBuilder createAlterTableBuilder() {
556
        return new H2SpatialAlterTableBuilderBase();
557
    }
558
            
559
    @Override
560
    protected TableNameBuilder createTableNameBuilder() {
561
        return new H2SpatialTableNameBuilderBase();
562
    }
563
    
564
    @Override
565
    protected CreateTableBuilder createCreateTableBuilder() {
566
        return new H2SpatialCreateTableBuilder();
567
    }
568

    
569
    @Override
570
    protected SelectBuilder createSelectBuilder() {
571
        return new H2SpatialSelectBuilderBase();
572
    }
573

    
574
    @Override
575
    protected CreateIndexBuilder createCreateIndexBuilder() {
576
        return new H2SpatialCreateIndexBuilder();
577
    }
578

    
579
}