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

History | View | Annotate | Download (14.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.gvsig.fmap.dal.DataTypes;
9
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
10
import org.gvsig.fmap.dal.feature.FeatureType;
11
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
12
import org.gvsig.fmap.dal.store.jdbc2.JDBCUtils;
13
import org.gvsig.fmap.dal.store.jdbc2.spi.JDBCSQLBuilderBase;
14
import org.gvsig.fmap.geom.Geometry;
15

    
16
public class H2SpatialSQLBuilder extends JDBCSQLBuilderBase {
17

    
18
    private final H2SpatialHelper helper;
19

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

    
43
        config.remove_functionality(SQLConfig.DELETE_GEOMETRY_COLUMN_FROM_TABLE_schema_table);
44
        config.remove_functionality(SQLConfig.DELETE_GEOMETRY_COLUMN_FROM_TABLE_table);
45
         
46
//        config.set(SQLConfig.UPDATE_TABLE_STATISTICS_table,"ANALYZE TABLE {0} SAMPLE_SIZE 0");
47
        config.set(SQLConfig.UPDATE_TABLE_STATISTICS_table,"ANALYZE SAMPLE_SIZE 0");
48

    
49
        config.set(SQLConfig.ST_GeomFromEWKB, "GeomFromWKB({0}, {1})");
50
        config.set(SQLConfig.ST_AsEWKB, "AsWKB(({0}))");        
51
        
52
        config.set(SQLConfig.lcase, "LOWER({0})");
53
        config.set(SQLConfig.ucase, "UPPER({0})");
54
        config.set(SQLConfig.operator_ILIKE, "({0}) ILIKE ({1})");
55
        config.set(SQLConfig.isNull, "( ({0}) IS NULL )");
56
        config.set(SQLConfig.notIsNull, "( ({0}) IS NOT NULL )");
57

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

    
82
    public class SpatiaLiteTableNameBuilderBase extends TableNameBuilderBase {
83

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

    
89
        @Override
90
        public boolean has_database() {
91
            return false;
92
        }
93
        
94
    }
95
    
96
    protected class SpatiaLiteCreateTableBuilder extends CreateTableBuilderBase {
97

    
98
        @Override
99
        public List<String> toStrings() {
100

    
101
            List<String> sqls = new ArrayList<>();
102
            StringBuilder builder = new StringBuilder();
103

    
104
            builder.append("CREATE TABLE ");
105
            builder.append(this.table.toString());
106
            builder.append(" (");
107
            boolean first = true;
108
            for (ColumnDescriptorBuilder column : columns) {
109

    
110
                if (first) {
111
                    first = false;
112
                } else {
113
                    builder.append(", ");
114
                }
115
                builder.append(identifier(column.getName()));
116
                builder.append(" ");
117
                builder.append(sqltype(column.getType(), column.getPrecision(), column.getSize()));
118
                if( column.isGeometry() ) {
119
                    int h2geomtype = getH2SpatialGeometryTypeFromGeometryType(
120
                        column.getGeometryType(), 
121
                        column.getGeometrySubtype()
122
                    );
123
                    if( h2geomtype>0 ) {
124
                        String constaint;
125
                        if( this.table().has_schema() ) {
126
                            constaint = MessageFormat.format(
127
                                "CHECK ({0}.ST_GEOMETRYTYPECODE({1}) = {2})",
128
                                this.table().getSchema(),
129
                                column.getName(),
130
                                h2geomtype
131
                            );                            
132
                        } else {
133
                            constaint = MessageFormat.format(
134
                                "CHECK (ST_GEOMETRYTYPECODE({0}) = {1})",
135
                                column.getName(),
136
                                h2geomtype
137
                            );
138
                        }
139
                        builder.append(constaint);
140
                    }
141
                } else {
142
                    if (column.isPrimaryKey()) {
143
                        builder.append(" PRIMARY KEY");
144
                    }
145
                    if( column.isAutomatic() ) {
146
                        builder.append(" AUTO_INCREMENT");
147
                    }
148
                    if (column.getDefaultValue() == null) {
149
                        if (column.allowNulls()) {
150
                            builder.append(" DEFAULT NULL");
151
                        }
152
                    } else {
153
                        if( column.getType() == DataTypes.DATE ) {
154
                            builder.append(" DEFAULT ( date('");
155
                            builder.append(column.getDefaultValue().toString());
156
                            builder.append("') )");
157
                        } else {
158
                            builder.append(" DEFAULT '");
159
                            builder.append(column.getDefaultValue().toString());
160
                            builder.append("'");
161
                        }
162
                }
163
                }
164
                if (!column.allowNulls()) {
165
                    builder.append(" NOT NULL");
166
                }
167
            }
168
            builder.append(" )");
169
            sqls.add(builder.toString());
170
            
171
            return sqls;
172
        }
173
        
174
        public int getH2SpatialGeometryTypeFromGeometryType(int geomtype, int geomsubtype) {
175
            //
176
            // https://github.com/orbisgis/h2gis/wiki/1.-Spatial-data#geometry-columns-view
177
            //
178
            
179
            switch(geomtype) {
180
            case Geometry.TYPES.POINT:
181
                return 1;
182
            case Geometry.TYPES.MULTIPOINT:
183
                return 4;
184
            case Geometry.TYPES.LINE:
185
                return 2;
186
            case Geometry.TYPES.MULTILINE:
187
                return 5;
188
            case Geometry.TYPES.POLYGON:
189
                return 3;
190
            case Geometry.TYPES.MULTIPOLYGON:
191
                return 6;
192
            default:
193
                return -1;
194
            }
195
        }
196
    }
197

    
198
    public class SpatiaLiteSelectBuilderBase extends SelectBuilderBase {
199
        
200
        protected boolean isValid(StringBuilder message) {
201
            if( message == null ) {
202
                message = new StringBuilder();
203
            }
204
            if( this.has_offset() && !this.has_order_by() ) {
205
                // Algunos gestores de BBDD requieren que se especifique un
206
                // orden para poder usar OFFSET. Como eso parece buena idea para
207
                // asegurar que siempre tengamos los mismo resultados, lo exijimos
208
                // siempre.
209
                message.append("Can't use OFFSET without an ORDER BY.");
210
                return false;
211
            }
212
            return true;
213
        }        
214
        
215
        @Override
216
        public String toString() {
217
            // SpatiaLite requiere que si se especifica OFFSET deba especificarse
218
            // LIMIT obligatoriamente. Se le puede poner un LIMIT -1 cuando 
219
            // queramos un OFFSET sin especificar un LIMIT.
220
            StringBuilder builder = new StringBuilder();
221
            if( !isValid(builder) ) {
222
                throw new IllegalStateException(builder.toString());
223
            }
224
            builder.append("SELECT ");
225
            if( this.distinct ) {
226
                builder.append("DISTINCT ");
227
            }
228
            boolean first = true;
229
            for (SelectColumnBuilder column : columns) {
230
                if (first) {
231
                    first = false;
232
                } else {
233
                    builder.append(", ");
234
                }
235
                builder.append(column.toString());
236
            }
237

    
238
            if ( this.has_from() ) {
239
                builder.append(" FROM ");
240
                builder.append(this.from.toString());
241
            }
242
            if ( this.has_where() ) {
243
                builder.append(" WHERE ");
244
                builder.append(this.where.toString());
245
            }
246
            
247
            if( this.has_order_by() ) {
248
                builder.append(" ORDER BY ");
249
                first = true;
250
                for (OrderByBuilder item : this.order_by) {
251
                    if (first) {
252
                        first = false;
253
                    } else {
254
                        builder.append(", ");
255
                    }
256
                    builder.append(item.toString());                    
257
                }   
258
            }
259
            
260
            if ( this.has_limit() && this.has_offset() ) {
261
                builder.append(" LIMIT ");
262
                builder.append(this.limit);
263
                builder.append(" OFFSET ");
264
                builder.append(this.offset);
265
                
266
            } else if ( this.has_limit()) {
267
                builder.append(" LIMIT ");
268
                builder.append(this.limit);
269

    
270
            } else if ( this.has_offset() ) {
271
                builder.append(" LIMIT -1 OFFSET ");
272
                builder.append(this.offset);    
273
            }
274
            return builder.toString();
275

    
276
        }
277
    }
278

    
279
    @Override
280
    public String bytearray(byte[] data) {
281
        // SpatiaLite usa un formato diferencte para especificar un array de 
282
        // bytes. En lugar de 0x... usa x'...' .
283
        StringBuilder builder = new StringBuilder();
284
        builder.append("x'");
285
        for (byte abyte : data) {
286
            int v = abyte & 0xff;
287
            builder.append(String.format("%02x", v));
288
        }
289
        builder.append("'");
290
        return builder.toString();
291
    }
292

    
293
    @Override
294
    public Object sqlgeometrydimension(int type, int subtype) {
295
        //'XY' or 2: 2D points, identified by X and Y coordinates
296
        //'XYM': 2D points, identified by X and Y coordinates. Each point stores an M-value (measure) as well
297
        //'XYZ' or 3: 3D points, identified by X, Y and Z coordinates
298
        //'XYZM' or 4: 3D points, identified by X, Y and Z coordinates. Each point stores an M-value (measure) as well
299
        switch(subtype) {
300
            case Geometry.SUBTYPES.GEOM2D:
301
            default:
302
                return "XY";
303
            case Geometry.SUBTYPES.GEOM2DM:
304
                return "XYM";
305
            case Geometry.SUBTYPES.GEOM3D:
306
                return "XYZ";
307
            case Geometry.SUBTYPES.GEOM3DM:
308
                return "XYZM";
309
        }
310
    }
311

    
312
    public H2SpatialHelper getHelper() {
313
        return this.helper;
314
    }
315
    
316
    @Override
317
    public void setParameters(PreparedStatement st, FeatureProvider feature) {
318
        try {
319
            FeatureType featureType = feature.getType();
320
            List<Object> values = new ArrayList<>();
321
            for (Parameter parameter : this.getParameters()) {
322
                if (parameter.is_constant()) {
323
                    values.add(parameter.getValue());
324
                } else {
325
                    String name = parameter.getName();
326
                    FeatureAttributeDescriptor descriptor = featureType.getAttributeDescriptor(name);
327
                    switch( descriptor.getType() ) {
328
                    case DataTypes.DATE:
329
                        Date value = (Date)(feature.get(name));
330
                        if( value == null ) {
331
                            values.add(null);
332
                        } else {
333
                            values.add(value.getTime());
334
                        }
335
                        break;
336
                    case DataTypes.GEOMETRY:
337
                        Geometry geom = this.getHelper().forceGeometryType(
338
                            descriptor.getGeomType(),
339
                            (Geometry)(feature.get(name))
340
                        );
341
                        values.add(geom);
342
                        break;
343
                    default:
344
                        values.add(feature.get(name));
345
                        break;
346
                    }
347
                }
348
            }
349
            JDBCUtils.setObjects(st, values, this.geometry_support_type());
350
        } catch (Exception ex) {
351
            String f = "unknow";
352
            try {
353
                f = feature.toString();
354
            } catch (Exception ex2) {
355
                // Do nothing
356
            }
357
            throw new RuntimeException("Can't set parameters to prepared statement from the feature (" + f + ")", ex);
358
        }
359
    }
360
            
361
    @Override
362
    protected TableNameBuilder createTableNameBuilder() {
363
        return new SpatiaLiteTableNameBuilderBase();
364
    }
365
    
366
    @Override
367
    protected CreateTableBuilder createCreateTableBuilder() {
368
        return new SpatiaLiteCreateTableBuilder();
369
    }
370

    
371
    @Override
372
    protected SelectBuilder createSelectBuilder() {
373
        return new SpatiaLiteSelectBuilderBase();
374
    }
375

    
376
}