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

History | View | Annotate | Download (21.5 KB)

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

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

    
22
public class H2SpatialSQLBuilder extends JDBCSQLBuilderBase {
23

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

    
41
        
42
        this.defaultSchema = "";
43
        this.supportSchemas = false;
44
        this.allowAutomaticValues = true;
45
        this.geometrySupportType = this.helper.getGeometrySupportType();
46
        this.hasSpatialFunctions = this.helper.hasSpatialFunctions();
47

    
48
        this.STMT_DELETE_GEOMETRY_COLUMN_FROM_TABLE_schema_table = null;
49
        this.STMT_DELETE_GEOMETRY_COLUMN_FROM_TABLE_table = null;
50
         
51
        this.STMT_UPDATE_TABLE_STATISTICS_table = "ANALYZE SAMPLE_SIZE 0";
52

    
53
//        config.set(SQLConfig.ST_AsBinary, "NVL2({0}, ST_AsBinary({0}), null)");
54

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

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

    
87
    public class H2SpatialTableNameBuilderBase extends TableNameBuilderBase {
88

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

    
94
        @Override
95
        public boolean has_database() {
96
            return false;
97
        }
98
        
99
    }
100

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

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

    
261
    }
262
    
263
    protected class H2SpatialCreateTableBuilder extends CreateTableBuilderBase {
264

    
265
        @Override
266
        public List<String> toStrings(Formatter formatter) {
267

    
268
            List<String> sqls = new ArrayList<>();
269
            StringBuilder builder = new StringBuilder();
270

    
271
            builder.append("CREATE TABLE ");
272
            builder.append(this.table.toString(formatter));
273
            builder.append(" (");
274
            boolean first = true;
275
            for (ColumnDescriptor column : columns) {
276

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

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

    
396
            if ( this.has_from() ) {
397
                builder.append(" FROM ");
398
                builder.append(this.from.toString(formatter));
399
            }
400
            if ( this.has_where() ) {
401
                builder.append(" WHERE ");
402
                builder.append(this.where.toString(formatter));
403
            }
404
            
405
            if( this.has_order_by() ) {
406
                builder.append(" ORDER BY ");
407
                first = true;
408
                for (OrderByBuilder item : this.order_by) {
409
                    if (first) {
410
                        first = false;
411
                    } else {
412
                        builder.append(", ");
413
                    }
414
                    builder.append(item.toString(formatter));                    
415
                }   
416
            }
417
            
418
            if ( this.has_limit() && this.has_offset() ) {
419
                builder.append(" LIMIT ");
420
                builder.append(this.limit);
421
                builder.append(" OFFSET ");
422
                builder.append(this.offset);
423
                
424
            } else if ( this.has_limit()) {
425
                builder.append(" LIMIT ");
426
                builder.append(this.limit);
427

    
428
            } else if ( this.has_offset() ) {
429
                builder.append(" LIMIT -1 OFFSET ");
430
                builder.append(this.offset);    
431
            }
432
            return builder.toString();
433

    
434
        }
435
    }
436

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

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

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

    
566
    @Override
567
    protected SelectBuilder createSelectBuilder() {
568
        return new H2SpatialSelectBuilderBase();
569
    }
570

    
571
    @Override
572
    protected CreateIndexBuilder createCreateIndexBuilder() {
573
        return new H2SpatialCreateIndexBuilder();
574
    }
575

    
576
}