1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.value; |
7 | |
8 | import java.io.BufferedReader; |
9 | import java.io.InputStream; |
10 | import java.io.Reader; |
11 | import java.math.BigDecimal; |
12 | import java.sql.Array; |
13 | import java.sql.Blob; |
14 | import java.sql.Clob; |
15 | import java.sql.Date; |
16 | import java.sql.ResultSet; |
17 | import java.sql.ResultSetMetaData; |
18 | import java.sql.SQLException; |
19 | import java.sql.Time; |
20 | import java.sql.Timestamp; |
21 | import java.sql.Types; |
22 | import java.util.ArrayList; |
23 | import java.util.HashMap; |
24 | import java.util.UUID; |
25 | |
26 | import org.h2.api.ErrorCode; |
27 | import org.h2.engine.Constants; |
28 | import org.h2.engine.SessionInterface; |
29 | import org.h2.engine.SysProperties; |
30 | import org.h2.jdbc.JdbcBlob; |
31 | import org.h2.jdbc.JdbcClob; |
32 | import org.h2.jdbc.JdbcConnection; |
33 | import org.h2.message.DbException; |
34 | import org.h2.tools.SimpleResultSet; |
35 | import org.h2.util.JdbcUtils; |
36 | import org.h2.util.New; |
37 | import org.h2.util.Utils; |
38 | |
39 | /** |
40 | * This class contains meta data information about data types, |
41 | * and can convert between Java objects and Values. |
42 | */ |
43 | public class DataType { |
44 | |
45 | /** |
46 | * This constant is used to represent the type of a ResultSet. There is no |
47 | * equivalent java.sql.Types value, but Oracle uses it to represent a |
48 | * ResultSet (OracleTypes.CURSOR = -10). |
49 | */ |
50 | public static final int TYPE_RESULT_SET = -10; |
51 | |
52 | /** |
53 | * The Geometry class. This object is null if the jts jar file is not in the |
54 | * classpath. |
55 | */ |
56 | public static final Class<?> GEOMETRY_CLASS; |
57 | |
58 | private static final String GEOMETRY_CLASS_NAME = |
59 | "com.vividsolutions.jts.geom.Geometry"; |
60 | |
61 | /** |
62 | * The list of types. An ArrayList so that Tomcat doesn't set it to null |
63 | * when clearing references. |
64 | */ |
65 | private static final ArrayList<DataType> TYPES = New.arrayList(); |
66 | private static final HashMap<String, DataType> TYPES_BY_NAME = New.hashMap(); |
67 | private static final ArrayList<DataType> TYPES_BY_VALUE_TYPE = New.arrayList(); |
68 | |
69 | /** |
70 | * The value type of this data type. |
71 | */ |
72 | public int type; |
73 | |
74 | /** |
75 | * The data type name. |
76 | */ |
77 | public String name; |
78 | |
79 | /** |
80 | * The SQL type. |
81 | */ |
82 | public int sqlType; |
83 | |
84 | /** |
85 | * The Java class name. |
86 | */ |
87 | public String jdbc; |
88 | |
89 | /** |
90 | * How closely the data type maps to the corresponding JDBC SQL type (low is |
91 | * best). |
92 | */ |
93 | public int sqlTypePos; |
94 | |
95 | /** |
96 | * The maximum supported precision. |
97 | */ |
98 | public long maxPrecision; |
99 | |
100 | /** |
101 | * The lowest possible scale. |
102 | */ |
103 | public int minScale; |
104 | |
105 | /** |
106 | * The highest possible scale. |
107 | */ |
108 | public int maxScale; |
109 | |
110 | /** |
111 | * If this is a numeric type. |
112 | */ |
113 | public boolean decimal; |
114 | |
115 | /** |
116 | * The prefix required for the SQL literal representation. |
117 | */ |
118 | public String prefix; |
119 | |
120 | /** |
121 | * The suffix required for the SQL literal representation. |
122 | */ |
123 | public String suffix; |
124 | |
125 | /** |
126 | * The list of parameters used in the column definition. |
127 | */ |
128 | public String params; |
129 | |
130 | /** |
131 | * If this is an autoincrement type. |
132 | */ |
133 | public boolean autoIncrement; |
134 | |
135 | /** |
136 | * If this data type is an autoincrement type. |
137 | */ |
138 | public boolean caseSensitive; |
139 | |
140 | /** |
141 | * If the precision parameter is supported. |
142 | */ |
143 | public boolean supportsPrecision; |
144 | |
145 | /** |
146 | * If the scale parameter is supported. |
147 | */ |
148 | public boolean supportsScale; |
149 | |
150 | /** |
151 | * The default precision. |
152 | */ |
153 | public long defaultPrecision; |
154 | |
155 | /** |
156 | * The default scale. |
157 | */ |
158 | public int defaultScale; |
159 | |
160 | /** |
161 | * The default display size. |
162 | */ |
163 | public int defaultDisplaySize; |
164 | |
165 | /** |
166 | * If this data type should not be listed in the database meta data. |
167 | */ |
168 | public boolean hidden; |
169 | |
170 | /** |
171 | * The number of bytes required for an object. |
172 | */ |
173 | public int memory; |
174 | |
175 | static { |
176 | Class<?> g; |
177 | try { |
178 | g = JdbcUtils.loadUserClass(GEOMETRY_CLASS_NAME); |
179 | } catch (Exception e) { |
180 | // class is not in the classpath - ignore |
181 | g = null; |
182 | } |
183 | GEOMETRY_CLASS = g; |
184 | } |
185 | |
186 | static { |
187 | for (int i = 0; i < Value.TYPE_COUNT; i++) { |
188 | TYPES_BY_VALUE_TYPE.add(null); |
189 | } |
190 | add(Value.NULL, Types.NULL, "Null", |
191 | new DataType(), |
192 | new String[]{"NULL"}, |
193 | // the value is always in the cache |
194 | 0 |
195 | ); |
196 | add(Value.STRING, Types.VARCHAR, "String", |
197 | createString(true), |
198 | new String[]{"VARCHAR", "VARCHAR2", "NVARCHAR", "NVARCHAR2", |
199 | "VARCHAR_CASESENSITIVE", "CHARACTER VARYING", "TID"}, |
200 | // 24 for ValueString, 24 for String |
201 | 48 |
202 | ); |
203 | add(Value.STRING, Types.LONGVARCHAR, "String", |
204 | createString(true), |
205 | new String[]{"LONGVARCHAR", "LONGNVARCHAR"}, |
206 | 48 |
207 | ); |
208 | add(Value.STRING_FIXED, Types.CHAR, "String", |
209 | createString(true), |
210 | new String[]{"CHAR", "CHARACTER", "NCHAR"}, |
211 | 48 |
212 | ); |
213 | add(Value.STRING_IGNORECASE, Types.VARCHAR, "String", |
214 | createString(false), |
215 | new String[]{"VARCHAR_IGNORECASE"}, |
216 | 48 |
217 | ); |
218 | add(Value.BOOLEAN, Types.BOOLEAN, "Boolean", |
219 | createDecimal(ValueBoolean.PRECISION, ValueBoolean.PRECISION, |
220 | 0, ValueBoolean.DISPLAY_SIZE, false, false), |
221 | new String[]{"BOOLEAN", "BIT", "BOOL"}, |
222 | // the value is always in the cache |
223 | 0 |
224 | ); |
225 | add(Value.BYTE, Types.TINYINT, "Byte", |
226 | createDecimal(ValueByte.PRECISION, ValueByte.PRECISION, 0, |
227 | ValueByte.DISPLAY_SIZE, false, false), |
228 | new String[]{"TINYINT"}, |
229 | // the value is almost always in the cache |
230 | 1 |
231 | ); |
232 | add(Value.SHORT, Types.SMALLINT, "Short", |
233 | createDecimal(ValueShort.PRECISION, ValueShort.PRECISION, 0, |
234 | ValueShort.DISPLAY_SIZE, false, false), |
235 | new String[]{"SMALLINT", "YEAR", "INT2"}, |
236 | // in many cases the value is in the cache |
237 | 20 |
238 | ); |
239 | add(Value.INT, Types.INTEGER, "Int", |
240 | createDecimal(ValueInt.PRECISION, ValueInt.PRECISION, 0, |
241 | ValueInt.DISPLAY_SIZE, false, false), |
242 | new String[]{"INTEGER", "INT", "MEDIUMINT", "INT4", "SIGNED"}, |
243 | // in many cases the value is in the cache |
244 | 20 |
245 | ); |
246 | add(Value.INT, Types.INTEGER, "Int", |
247 | createDecimal(ValueInt.PRECISION, ValueInt.PRECISION, 0, |
248 | ValueInt.DISPLAY_SIZE, false, true), |
249 | new String[]{"SERIAL"}, |
250 | 20 |
251 | ); |
252 | add(Value.LONG, Types.BIGINT, "Long", |
253 | createDecimal(ValueLong.PRECISION, ValueLong.PRECISION, 0, |
254 | ValueLong.DISPLAY_SIZE, false, false), |
255 | new String[]{"BIGINT", "INT8", "LONG"}, |
256 | 24 |
257 | ); |
258 | add(Value.LONG, Types.BIGINT, "Long", |
259 | createDecimal(ValueLong.PRECISION, ValueLong.PRECISION, 0, |
260 | ValueLong.DISPLAY_SIZE, false, true), |
261 | new String[]{"IDENTITY", "BIGSERIAL"}, |
262 | 24 |
263 | ); |
264 | add(Value.DECIMAL, Types.DECIMAL, "BigDecimal", |
265 | createDecimal(Integer.MAX_VALUE, |
266 | ValueDecimal.DEFAULT_PRECISION, |
267 | ValueDecimal.DEFAULT_SCALE, |
268 | ValueDecimal.DEFAULT_DISPLAY_SIZE, true, false), |
269 | new String[]{"DECIMAL", "DEC"}, |
270 | // 40 for ValueDecimal, |
271 | 64 |
272 | ); |
273 | add(Value.DECIMAL, Types.NUMERIC, "BigDecimal", |
274 | createDecimal(Integer.MAX_VALUE, |
275 | ValueDecimal.DEFAULT_PRECISION, |
276 | ValueDecimal.DEFAULT_SCALE, |
277 | ValueDecimal.DEFAULT_DISPLAY_SIZE, true, false), |
278 | new String[]{"NUMERIC", "NUMBER"}, |
279 | 64 |
280 | ); |
281 | add(Value.FLOAT, Types.REAL, "Float", |
282 | createDecimal(ValueFloat.PRECISION, ValueFloat.PRECISION, |
283 | 0, ValueFloat.DISPLAY_SIZE, false, false), |
284 | new String[] {"REAL", "FLOAT4"}, |
285 | 24 |
286 | ); |
287 | add(Value.DOUBLE, Types.DOUBLE, "Double", |
288 | createDecimal(ValueDouble.PRECISION, ValueDouble.PRECISION, |
289 | 0, ValueDouble.DISPLAY_SIZE, false, false), |
290 | new String[] { "DOUBLE", "DOUBLE PRECISION" }, |
291 | 24 |
292 | ); |
293 | add(Value.DOUBLE, Types.FLOAT, "Double", |
294 | createDecimal(ValueDouble.PRECISION, ValueDouble.PRECISION, |
295 | 0, ValueDouble.DISPLAY_SIZE, false, false), |
296 | new String[] {"FLOAT", "FLOAT8" }, |
297 | 24 |
298 | ); |
299 | add(Value.TIME, Types.TIME, "Time", |
300 | createDate(ValueTime.PRECISION, "TIME", 0, ValueTime.DISPLAY_SIZE), |
301 | new String[]{"TIME"}, |
302 | // 24 for ValueTime, 32 for java.sql.Time |
303 | 56 |
304 | ); |
305 | add(Value.DATE, Types.DATE, "Date", |
306 | createDate(ValueDate.PRECISION, "DATE", 0, ValueDate.DISPLAY_SIZE), |
307 | new String[]{"DATE"}, |
308 | // 24 for ValueDate, 32 for java.sql.Data |
309 | 56 |
310 | ); |
311 | add(Value.TIMESTAMP, Types.TIMESTAMP, "Timestamp", |
312 | createDate(ValueTimestamp.PRECISION, "TIMESTAMP", |
313 | ValueTimestamp.DEFAULT_SCALE, ValueTimestamp.DISPLAY_SIZE), |
314 | new String[]{"TIMESTAMP", "DATETIME", "DATETIME2", "SMALLDATETIME"}, |
315 | // 24 for ValueTimestamp, 32 for java.sql.Timestamp |
316 | 56 |
317 | ); |
318 | add(Value.BYTES, Types.VARBINARY, "Bytes", |
319 | createString(false), |
320 | new String[]{"VARBINARY"}, |
321 | 32 |
322 | ); |
323 | add(Value.BYTES, Types.BINARY, "Bytes", |
324 | createString(false), |
325 | new String[]{"BINARY", "RAW", "BYTEA", "LONG RAW"}, |
326 | 32 |
327 | ); |
328 | add(Value.BYTES, Types.LONGVARBINARY, "Bytes", |
329 | createString(false), |
330 | new String[]{"LONGVARBINARY"}, |
331 | 32 |
332 | ); |
333 | add(Value.UUID, Types.BINARY, "Bytes", |
334 | createString(false), |
335 | new String[]{"UUID"}, |
336 | 32 |
337 | ); |
338 | add(Value.JAVA_OBJECT, Types.OTHER, "Object", |
339 | createString(false), |
340 | new String[]{"OTHER", "OBJECT", "JAVA_OBJECT"}, |
341 | 24 |
342 | ); |
343 | add(Value.BLOB, Types.BLOB, "Blob", |
344 | createLob(), |
345 | new String[]{"BLOB", "TINYBLOB", "MEDIUMBLOB", |
346 | "LONGBLOB", "IMAGE", "OID"}, |
347 | // 80 for ValueLob, 24 for String |
348 | 104 |
349 | ); |
350 | add(Value.CLOB, Types.CLOB, "Clob", |
351 | createLob(), |
352 | new String[]{"CLOB", "TINYTEXT", "TEXT", "MEDIUMTEXT", |
353 | "LONGTEXT", "NTEXT", "NCLOB"}, |
354 | // 80 for ValueLob, 24 for String |
355 | 104 |
356 | ); |
357 | add(Value.GEOMETRY, Types.OTHER, "Geometry", |
358 | createString(false), |
359 | new String[]{"GEOMETRY"}, |
360 | 32 |
361 | ); |
362 | DataType dataType = new DataType(); |
363 | dataType.prefix = "("; |
364 | dataType.suffix = "')"; |
365 | add(Value.ARRAY, Types.ARRAY, "Array", |
366 | dataType, |
367 | new String[]{"ARRAY"}, |
368 | 32 |
369 | ); |
370 | dataType = new DataType(); |
371 | add(Value.RESULT_SET, DataType.TYPE_RESULT_SET, "ResultSet", |
372 | dataType, |
373 | new String[]{"RESULT_SET"}, |
374 | 400 |
375 | ); |
376 | for (int i = 0, size = TYPES_BY_VALUE_TYPE.size(); i < size; i++) { |
377 | DataType dt = TYPES_BY_VALUE_TYPE.get(i); |
378 | if (dt == null) { |
379 | DbException.throwInternalError("unmapped type " + i); |
380 | } |
381 | Value.getOrder(i); |
382 | } |
383 | } |
384 | |
385 | private static void add(int type, int sqlType, String jdbc, |
386 | DataType dataType, String[] names, int memory) { |
387 | for (int i = 0; i < names.length; i++) { |
388 | DataType dt = new DataType(); |
389 | dt.type = type; |
390 | dt.sqlType = sqlType; |
391 | dt.jdbc = jdbc; |
392 | dt.name = names[i]; |
393 | dt.autoIncrement = dataType.autoIncrement; |
394 | dt.decimal = dataType.decimal; |
395 | dt.maxPrecision = dataType.maxPrecision; |
396 | dt.maxScale = dataType.maxScale; |
397 | dt.minScale = dataType.minScale; |
398 | dt.params = dataType.params; |
399 | dt.prefix = dataType.prefix; |
400 | dt.suffix = dataType.suffix; |
401 | dt.supportsPrecision = dataType.supportsPrecision; |
402 | dt.supportsScale = dataType.supportsScale; |
403 | dt.defaultPrecision = dataType.defaultPrecision; |
404 | dt.defaultScale = dataType.defaultScale; |
405 | dt.defaultDisplaySize = dataType.defaultDisplaySize; |
406 | dt.caseSensitive = dataType.caseSensitive; |
407 | dt.hidden = i > 0; |
408 | dt.memory = memory; |
409 | for (DataType t2 : TYPES) { |
410 | if (t2.sqlType == dt.sqlType) { |
411 | dt.sqlTypePos++; |
412 | } |
413 | } |
414 | TYPES_BY_NAME.put(dt.name, dt); |
415 | if (TYPES_BY_VALUE_TYPE.get(type) == null) { |
416 | TYPES_BY_VALUE_TYPE.set(type, dt); |
417 | } |
418 | TYPES.add(dt); |
419 | } |
420 | } |
421 | |
422 | private static DataType createDecimal(int maxPrecision, |
423 | int defaultPrecision, int defaultScale, int defaultDisplaySize, |
424 | boolean needsPrecisionAndScale, boolean autoInc) { |
425 | DataType dataType = new DataType(); |
426 | dataType.maxPrecision = maxPrecision; |
427 | dataType.defaultPrecision = defaultPrecision; |
428 | dataType.defaultScale = defaultScale; |
429 | dataType.defaultDisplaySize = defaultDisplaySize; |
430 | if (needsPrecisionAndScale) { |
431 | dataType.params = "PRECISION,SCALE"; |
432 | dataType.supportsPrecision = true; |
433 | dataType.supportsScale = true; |
434 | } |
435 | dataType.decimal = true; |
436 | dataType.autoIncrement = autoInc; |
437 | return dataType; |
438 | } |
439 | |
440 | private static DataType createDate(int precision, String prefix, int scale, |
441 | int displaySize) { |
442 | DataType dataType = new DataType(); |
443 | dataType.prefix = prefix + " '"; |
444 | dataType.suffix = "'"; |
445 | dataType.maxPrecision = precision; |
446 | dataType.supportsScale = scale != 0; |
447 | dataType.maxScale = scale; |
448 | dataType.defaultPrecision = precision; |
449 | dataType.defaultScale = scale; |
450 | dataType.defaultDisplaySize = displaySize; |
451 | return dataType; |
452 | } |
453 | |
454 | private static DataType createString(boolean caseSensitive) { |
455 | DataType dataType = new DataType(); |
456 | dataType.prefix = "'"; |
457 | dataType.suffix = "'"; |
458 | dataType.params = "LENGTH"; |
459 | dataType.caseSensitive = caseSensitive; |
460 | dataType.supportsPrecision = true; |
461 | dataType.maxPrecision = Integer.MAX_VALUE; |
462 | dataType.defaultPrecision = Integer.MAX_VALUE; |
463 | dataType.defaultDisplaySize = Integer.MAX_VALUE; |
464 | return dataType; |
465 | } |
466 | |
467 | private static DataType createLob() { |
468 | DataType t = createString(true); |
469 | t.maxPrecision = Long.MAX_VALUE; |
470 | t.defaultPrecision = Long.MAX_VALUE; |
471 | return t; |
472 | } |
473 | |
474 | /** |
475 | * Get the list of data types. |
476 | * |
477 | * @return the list |
478 | */ |
479 | public static ArrayList<DataType> getTypes() { |
480 | return TYPES; |
481 | } |
482 | |
483 | /** |
484 | * Read a value from the given result set. |
485 | * |
486 | * @param session the session |
487 | * @param rs the result set |
488 | * @param columnIndex the column index (1 based) |
489 | * @param type the data type |
490 | * @return the value |
491 | */ |
492 | public static Value readValue(SessionInterface session, ResultSet rs, |
493 | int columnIndex, int type) { |
494 | try { |
495 | Value v; |
496 | switch(type) { |
497 | case Value.NULL: { |
498 | return ValueNull.INSTANCE; |
499 | } |
500 | case Value.BYTES: { |
501 | byte[] buff = rs.getBytes(columnIndex); |
502 | v = buff == null ? (Value) ValueNull.INSTANCE : |
503 | ValueBytes.getNoCopy(buff); |
504 | break; |
505 | } |
506 | case Value.UUID: { |
507 | byte[] buff = rs.getBytes(columnIndex); |
508 | v = buff == null ? (Value) ValueNull.INSTANCE : |
509 | ValueUuid.get(buff); |
510 | break; |
511 | } |
512 | case Value.BOOLEAN: { |
513 | boolean value = rs.getBoolean(columnIndex); |
514 | v = rs.wasNull() ? (Value) ValueNull.INSTANCE : |
515 | ValueBoolean.get(value); |
516 | break; |
517 | } |
518 | case Value.BYTE: { |
519 | byte value = rs.getByte(columnIndex); |
520 | v = rs.wasNull() ? (Value) ValueNull.INSTANCE : |
521 | ValueByte.get(value); |
522 | break; |
523 | } |
524 | case Value.DATE: { |
525 | Date value = rs.getDate(columnIndex); |
526 | v = value == null ? (Value) ValueNull.INSTANCE : |
527 | ValueDate.get(value); |
528 | break; |
529 | } |
530 | case Value.TIME: { |
531 | Time value = rs.getTime(columnIndex); |
532 | v = value == null ? (Value) ValueNull.INSTANCE : |
533 | ValueTime.get(value); |
534 | break; |
535 | } |
536 | case Value.TIMESTAMP: { |
537 | Timestamp value = rs.getTimestamp(columnIndex); |
538 | v = value == null ? (Value) ValueNull.INSTANCE : |
539 | ValueTimestamp.get(value); |
540 | break; |
541 | } |
542 | case Value.DECIMAL: { |
543 | BigDecimal value = rs.getBigDecimal(columnIndex); |
544 | v = value == null ? (Value) ValueNull.INSTANCE : |
545 | ValueDecimal.get(value); |
546 | break; |
547 | } |
548 | case Value.DOUBLE: { |
549 | double value = rs.getDouble(columnIndex); |
550 | v = rs.wasNull() ? (Value) ValueNull.INSTANCE : |
551 | ValueDouble.get(value); |
552 | break; |
553 | } |
554 | case Value.FLOAT: { |
555 | float value = rs.getFloat(columnIndex); |
556 | v = rs.wasNull() ? (Value) ValueNull.INSTANCE : |
557 | ValueFloat.get(value); |
558 | break; |
559 | } |
560 | case Value.INT: { |
561 | int value = rs.getInt(columnIndex); |
562 | v = rs.wasNull() ? (Value) ValueNull.INSTANCE : |
563 | ValueInt.get(value); |
564 | break; |
565 | } |
566 | case Value.LONG: { |
567 | long value = rs.getLong(columnIndex); |
568 | v = rs.wasNull() ? (Value) ValueNull.INSTANCE : |
569 | ValueLong.get(value); |
570 | break; |
571 | } |
572 | case Value.SHORT: { |
573 | short value = rs.getShort(columnIndex); |
574 | v = rs.wasNull() ? (Value) ValueNull.INSTANCE : |
575 | ValueShort.get(value); |
576 | break; |
577 | } |
578 | case Value.STRING_IGNORECASE: { |
579 | String s = rs.getString(columnIndex); |
580 | v = (s == null) ? (Value) ValueNull.INSTANCE : |
581 | ValueStringIgnoreCase.get(s); |
582 | break; |
583 | } |
584 | case Value.STRING_FIXED: { |
585 | String s = rs.getString(columnIndex); |
586 | v = (s == null) ? (Value) ValueNull.INSTANCE : |
587 | ValueStringFixed.get(s); |
588 | break; |
589 | } |
590 | case Value.STRING: { |
591 | String s = rs.getString(columnIndex); |
592 | v = (s == null) ? (Value) ValueNull.INSTANCE : |
593 | ValueString.get(s); |
594 | break; |
595 | } |
596 | case Value.CLOB: { |
597 | if (session == null) { |
598 | v = ValueLobDb.createSmallLob( |
599 | Value.CLOB, rs.getString(columnIndex).getBytes(Constants.UTF8)); |
600 | } else { |
601 | Reader in = rs.getCharacterStream(columnIndex); |
602 | if (in == null) { |
603 | v = ValueNull.INSTANCE; |
604 | } else { |
605 | v = session.getDataHandler().getLobStorage(). |
606 | createClob(new BufferedReader(in), -1); |
607 | } |
608 | } |
609 | break; |
610 | } |
611 | case Value.BLOB: { |
612 | if (session == null) { |
613 | v = ValueLobDb.createSmallLob( |
614 | Value.BLOB, rs.getBytes(columnIndex)); |
615 | } else { |
616 | InputStream in = rs.getBinaryStream(columnIndex); |
617 | v = (in == null) ? (Value) ValueNull.INSTANCE : |
618 | session.getDataHandler().getLobStorage().createBlob(in, -1); |
619 | } |
620 | break; |
621 | } |
622 | case Value.JAVA_OBJECT: { |
623 | if (SysProperties.serializeJavaObject) { |
624 | byte[] buff = rs.getBytes(columnIndex); |
625 | v = buff == null ? ValueNull.INSTANCE : |
626 | ValueJavaObject.getNoCopy(null, buff, session.getDataHandler()); |
627 | } else { |
628 | Object o = rs.getObject(columnIndex); |
629 | v = o == null ? ValueNull.INSTANCE : |
630 | ValueJavaObject.getNoCopy(o, null, session.getDataHandler()); |
631 | } |
632 | break; |
633 | } |
634 | case Value.ARRAY: { |
635 | Array array = rs.getArray(columnIndex); |
636 | if (array == null) { |
637 | return ValueNull.INSTANCE; |
638 | } |
639 | Object[] list = (Object[]) array.getArray(); |
640 | if (list == null) { |
641 | return ValueNull.INSTANCE; |
642 | } |
643 | int len = list.length; |
644 | Value[] values = new Value[len]; |
645 | for (int i = 0; i < len; i++) { |
646 | values[i] = DataType.convertToValue(session, list[i], Value.NULL); |
647 | } |
648 | v = ValueArray.get(values); |
649 | break; |
650 | } |
651 | case Value.RESULT_SET: { |
652 | ResultSet x = (ResultSet) rs.getObject(columnIndex); |
653 | if (x == null) { |
654 | return ValueNull.INSTANCE; |
655 | } |
656 | return ValueResultSet.get(rs); |
657 | } |
658 | case Value.GEOMETRY: { |
659 | Object x = rs.getObject(columnIndex); |
660 | if (x == null) { |
661 | return ValueNull.INSTANCE; |
662 | } |
663 | return ValueGeometry.getFromGeometry(x); |
664 | } |
665 | default: |
666 | throw DbException.throwInternalError("type="+type); |
667 | } |
668 | return v; |
669 | } catch (SQLException e) { |
670 | throw DbException.convert(e); |
671 | } |
672 | } |
673 | |
674 | /** |
675 | * Get the name of the Java class for the given value type. |
676 | * |
677 | * @param type the value type |
678 | * @return the class name |
679 | */ |
680 | public static String getTypeClassName(int type) { |
681 | switch(type) { |
682 | case Value.BOOLEAN: |
683 | // "java.lang.Boolean"; |
684 | return Boolean.class.getName(); |
685 | case Value.BYTE: |
686 | // "java.lang.Byte"; |
687 | return Byte.class.getName(); |
688 | case Value.SHORT: |
689 | // "java.lang.Short"; |
690 | return Short.class.getName(); |
691 | case Value.INT: |
692 | // "java.lang.Integer"; |
693 | return Integer.class.getName(); |
694 | case Value.LONG: |
695 | // "java.lang.Long"; |
696 | return Long.class.getName(); |
697 | case Value.DECIMAL: |
698 | // "java.math.BigDecimal"; |
699 | return BigDecimal.class.getName(); |
700 | case Value.TIME: |
701 | // "java.sql.Time"; |
702 | return Time.class.getName(); |
703 | case Value.DATE: |
704 | // "java.sql.Date"; |
705 | return Date.class.getName(); |
706 | case Value.TIMESTAMP: |
707 | // "java.sql.Timestamp"; |
708 | return Timestamp.class.getName(); |
709 | case Value.BYTES: |
710 | case Value.UUID: |
711 | // "[B", not "byte[]"; |
712 | return byte[].class.getName(); |
713 | case Value.STRING: |
714 | case Value.STRING_IGNORECASE: |
715 | case Value.STRING_FIXED: |
716 | // "java.lang.String"; |
717 | return String.class.getName(); |
718 | case Value.BLOB: |
719 | // "java.sql.Blob"; |
720 | return java.sql.Blob.class.getName(); |
721 | case Value.CLOB: |
722 | // "java.sql.Clob"; |
723 | return java.sql.Clob.class.getName(); |
724 | case Value.DOUBLE: |
725 | // "java.lang.Double"; |
726 | return Double.class.getName(); |
727 | case Value.FLOAT: |
728 | // "java.lang.Float"; |
729 | return Float.class.getName(); |
730 | case Value.NULL: |
731 | return null; |
732 | case Value.JAVA_OBJECT: |
733 | // "java.lang.Object"; |
734 | return Object.class.getName(); |
735 | case Value.UNKNOWN: |
736 | // anything |
737 | return Object.class.getName(); |
738 | case Value.ARRAY: |
739 | return Array.class.getName(); |
740 | case Value.RESULT_SET: |
741 | return ResultSet.class.getName(); |
742 | case Value.GEOMETRY: |
743 | return GEOMETRY_CLASS_NAME; |
744 | default: |
745 | throw DbException.throwInternalError("type="+type); |
746 | } |
747 | } |
748 | |
749 | /** |
750 | * Get the data type object for the given value type. |
751 | * |
752 | * @param type the value type |
753 | * @return the data type object |
754 | */ |
755 | public static DataType getDataType(int type) { |
756 | if (type == Value.UNKNOWN) { |
757 | throw DbException.get(ErrorCode.UNKNOWN_DATA_TYPE_1, "?"); |
758 | } |
759 | DataType dt = TYPES_BY_VALUE_TYPE.get(type); |
760 | if (dt == null) { |
761 | dt = TYPES_BY_VALUE_TYPE.get(Value.NULL); |
762 | } |
763 | return dt; |
764 | } |
765 | |
766 | /** |
767 | * Convert a value type to a SQL type. |
768 | * |
769 | * @param type the value type |
770 | * @return the SQL type |
771 | */ |
772 | public static int convertTypeToSQLType(int type) { |
773 | return getDataType(type).sqlType; |
774 | } |
775 | |
776 | /** |
777 | * Convert a SQL type to a value type using SQL type name, in order to |
778 | * manage SQL type extension mechanism. |
779 | * |
780 | * @param sqlType the SQL type |
781 | * @param sqlTypeName the SQL type name |
782 | * @return the value type |
783 | */ |
784 | private static int convertSQLTypeToValueType(int sqlType, String sqlTypeName) { |
785 | switch (sqlType) { |
786 | case Types.OTHER: |
787 | case Types.JAVA_OBJECT: |
788 | if (sqlTypeName.equalsIgnoreCase("geometry")) { |
789 | return Value.GEOMETRY; |
790 | } |
791 | } |
792 | return convertSQLTypeToValueType(sqlType); |
793 | } |
794 | |
795 | /** |
796 | * Get the SQL type from the result set meta data for the given column. This |
797 | * method uses the SQL type and type name. |
798 | * |
799 | * @param meta the meta data |
800 | * @param columnIndex the column index (1, 2,...) |
801 | * @return the value type |
802 | */ |
803 | public static int getValueTypeFromResultSet(ResultSetMetaData meta, |
804 | int columnIndex) throws SQLException { |
805 | return convertSQLTypeToValueType( |
806 | meta.getColumnType(columnIndex), |
807 | meta.getColumnTypeName(columnIndex)); |
808 | } |
809 | |
810 | /** |
811 | * Convert a SQL type to a value type. |
812 | * |
813 | * @param sqlType the SQL type |
814 | * @return the value type |
815 | */ |
816 | public static int convertSQLTypeToValueType(int sqlType) { |
817 | switch(sqlType) { |
818 | case Types.CHAR: |
819 | case Types.NCHAR: |
820 | return Value.STRING_FIXED; |
821 | case Types.VARCHAR: |
822 | case Types.LONGVARCHAR: |
823 | case Types.NVARCHAR: |
824 | case Types.LONGNVARCHAR: |
825 | return Value.STRING; |
826 | case Types.NUMERIC: |
827 | case Types.DECIMAL: |
828 | return Value.DECIMAL; |
829 | case Types.BIT: |
830 | case Types.BOOLEAN: |
831 | return Value.BOOLEAN; |
832 | case Types.INTEGER: |
833 | return Value.INT; |
834 | case Types.SMALLINT: |
835 | return Value.SHORT; |
836 | case Types.TINYINT: |
837 | return Value.BYTE; |
838 | case Types.BIGINT: |
839 | return Value.LONG; |
840 | case Types.REAL: |
841 | return Value.FLOAT; |
842 | case Types.DOUBLE: |
843 | case Types.FLOAT: |
844 | return Value.DOUBLE; |
845 | case Types.BINARY: |
846 | case Types.VARBINARY: |
847 | case Types.LONGVARBINARY: |
848 | return Value.BYTES; |
849 | case Types.OTHER: |
850 | case Types.JAVA_OBJECT: |
851 | return Value.JAVA_OBJECT; |
852 | case Types.DATE: |
853 | return Value.DATE; |
854 | case Types.TIME: |
855 | return Value.TIME; |
856 | case Types.TIMESTAMP: |
857 | return Value.TIMESTAMP; |
858 | case Types.BLOB: |
859 | return Value.BLOB; |
860 | case Types.CLOB: |
861 | case Types.NCLOB: |
862 | return Value.CLOB; |
863 | case Types.NULL: |
864 | return Value.NULL; |
865 | case Types.ARRAY: |
866 | return Value.ARRAY; |
867 | case DataType.TYPE_RESULT_SET: |
868 | return Value.RESULT_SET; |
869 | default: |
870 | throw DbException.get( |
871 | ErrorCode.UNKNOWN_DATA_TYPE_1, "" + sqlType); |
872 | } |
873 | } |
874 | |
875 | /** |
876 | * Get the value type for the given Java class. |
877 | * |
878 | * @param x the Java class |
879 | * @return the value type |
880 | */ |
881 | public static int getTypeFromClass(Class <?> x) { |
882 | // TODO refactor: too many if/else in functions, can reduce! |
883 | if (x == null || Void.TYPE == x) { |
884 | return Value.NULL; |
885 | } |
886 | if (x.isPrimitive()) { |
887 | x = Utils.getNonPrimitiveClass(x); |
888 | } |
889 | if (String.class == x) { |
890 | return Value.STRING; |
891 | } else if (Integer.class == x) { |
892 | return Value.INT; |
893 | } else if (Long.class == x) { |
894 | return Value.LONG; |
895 | } else if (Boolean.class == x) { |
896 | return Value.BOOLEAN; |
897 | } else if (Double.class == x) { |
898 | return Value.DOUBLE; |
899 | } else if (Byte.class == x) { |
900 | return Value.BYTE; |
901 | } else if (Short.class == x) { |
902 | return Value.SHORT; |
903 | } else if (Character.class == x) { |
904 | throw DbException.get( |
905 | ErrorCode.DATA_CONVERSION_ERROR_1, "char (not supported)"); |
906 | } else if (Float.class == x) { |
907 | return Value.FLOAT; |
908 | } else if (byte[].class == x) { |
909 | return Value.BYTES; |
910 | } else if (UUID.class == x) { |
911 | return Value.UUID; |
912 | } else if (Void.class == x) { |
913 | return Value.NULL; |
914 | } else if (BigDecimal.class.isAssignableFrom(x)) { |
915 | return Value.DECIMAL; |
916 | } else if (ResultSet.class.isAssignableFrom(x)) { |
917 | return Value.RESULT_SET; |
918 | } else if (Value.ValueBlob.class.isAssignableFrom(x)) { |
919 | return Value.BLOB; |
920 | } else if (Value.ValueClob.class.isAssignableFrom(x)) { |
921 | return Value.CLOB; |
922 | } else if (Date.class.isAssignableFrom(x)) { |
923 | return Value.DATE; |
924 | } else if (Time.class.isAssignableFrom(x)) { |
925 | return Value.TIME; |
926 | } else if (Timestamp.class.isAssignableFrom(x)) { |
927 | return Value.TIMESTAMP; |
928 | } else if (java.util.Date.class.isAssignableFrom(x)) { |
929 | return Value.TIMESTAMP; |
930 | } else if (java.io.Reader.class.isAssignableFrom(x)) { |
931 | return Value.CLOB; |
932 | } else if (java.sql.Clob.class.isAssignableFrom(x)) { |
933 | return Value.CLOB; |
934 | } else if (java.io.InputStream.class.isAssignableFrom(x)) { |
935 | return Value.BLOB; |
936 | } else if (java.sql.Blob.class.isAssignableFrom(x)) { |
937 | return Value.BLOB; |
938 | } else if (Object[].class.isAssignableFrom(x)) { |
939 | // this includes String[] and so on |
940 | return Value.ARRAY; |
941 | } else if (isGeometryClass(x)) { |
942 | return Value.GEOMETRY; |
943 | } else { |
944 | return Value.JAVA_OBJECT; |
945 | } |
946 | } |
947 | |
948 | /** |
949 | * Convert a Java object to a value. |
950 | * |
951 | * @param session the session |
952 | * @param x the value |
953 | * @param type the value type |
954 | * @return the value |
955 | */ |
956 | public static Value convertToValue(SessionInterface session, Object x, |
957 | int type) { |
958 | if (x == null) { |
959 | return ValueNull.INSTANCE; |
960 | } |
961 | if (type == Value.JAVA_OBJECT) { |
962 | return ValueJavaObject.getNoCopy(x, null, session.getDataHandler()); |
963 | } |
964 | if (x instanceof String) { |
965 | return ValueString.get((String) x); |
966 | } else if (x instanceof Value) { |
967 | return (Value) x; |
968 | } else if (x instanceof Long) { |
969 | return ValueLong.get(((Long) x).longValue()); |
970 | } else if (x instanceof Integer) { |
971 | return ValueInt.get(((Integer) x).intValue()); |
972 | } else if (x instanceof BigDecimal) { |
973 | return ValueDecimal.get((BigDecimal) x); |
974 | } else if (x instanceof Boolean) { |
975 | return ValueBoolean.get(((Boolean) x).booleanValue()); |
976 | } else if (x instanceof Byte) { |
977 | return ValueByte.get(((Byte) x).byteValue()); |
978 | } else if (x instanceof Short) { |
979 | return ValueShort.get(((Short) x).shortValue()); |
980 | } else if (x instanceof Float) { |
981 | return ValueFloat.get(((Float) x).floatValue()); |
982 | } else if (x instanceof Double) { |
983 | return ValueDouble.get(((Double) x).doubleValue()); |
984 | } else if (x instanceof byte[]) { |
985 | return ValueBytes.get((byte[]) x); |
986 | } else if (x instanceof Date) { |
987 | return ValueDate.get((Date) x); |
988 | } else if (x instanceof Time) { |
989 | return ValueTime.get((Time) x); |
990 | } else if (x instanceof Timestamp) { |
991 | return ValueTimestamp.get((Timestamp) x); |
992 | } else if (x instanceof java.util.Date) { |
993 | return ValueTimestamp.fromMillis(((java.util.Date) x).getTime()); |
994 | } else if (x instanceof java.io.Reader) { |
995 | Reader r = new BufferedReader((java.io.Reader) x); |
996 | return session.getDataHandler().getLobStorage(). |
997 | createClob(r, -1); |
998 | } else if (x instanceof java.sql.Clob) { |
999 | try { |
1000 | Reader r = new BufferedReader( |
1001 | ((java.sql.Clob) x).getCharacterStream()); |
1002 | return session.getDataHandler().getLobStorage(). |
1003 | createClob(r, -1); |
1004 | } catch (SQLException e) { |
1005 | throw DbException.convert(e); |
1006 | } |
1007 | } else if (x instanceof java.io.InputStream) { |
1008 | return session.getDataHandler().getLobStorage(). |
1009 | createBlob((java.io.InputStream) x, -1); |
1010 | } else if (x instanceof java.sql.Blob) { |
1011 | try { |
1012 | return session.getDataHandler().getLobStorage(). |
1013 | createBlob(((java.sql.Blob) x).getBinaryStream(), -1); |
1014 | } catch (SQLException e) { |
1015 | throw DbException.convert(e); |
1016 | } |
1017 | } else if (x instanceof ResultSet) { |
1018 | if (x instanceof SimpleResultSet) { |
1019 | return ValueResultSet.get((ResultSet) x); |
1020 | } |
1021 | return ValueResultSet.getCopy((ResultSet) x, Integer.MAX_VALUE); |
1022 | } else if (x instanceof UUID) { |
1023 | UUID u = (UUID) x; |
1024 | return ValueUuid.get(u.getMostSignificantBits(), u.getLeastSignificantBits()); |
1025 | } else if (x instanceof Object[]) { |
1026 | // (a.getClass().isArray()); |
1027 | // (a.getClass().getComponentType().isPrimitive()); |
1028 | Object[] o = (Object[]) x; |
1029 | int len = o.length; |
1030 | Value[] v = new Value[len]; |
1031 | for (int i = 0; i < len; i++) { |
1032 | v[i] = convertToValue(session, o[i], type); |
1033 | } |
1034 | return ValueArray.get(x.getClass().getComponentType(), v); |
1035 | } else if (x instanceof Character) { |
1036 | return ValueStringFixed.get(((Character) x).toString()); |
1037 | } else if (isGeometry(x)) { |
1038 | return ValueGeometry.getFromGeometry(x); |
1039 | } else { |
1040 | return ValueJavaObject.getNoCopy(x, null, session.getDataHandler()); |
1041 | } |
1042 | } |
1043 | |
1044 | /** |
1045 | * Check whether a given class matches the Geometry class. |
1046 | * |
1047 | * @param x the class |
1048 | * @return true if it is a Geometry class |
1049 | */ |
1050 | public static boolean isGeometryClass(Class<?> x) { |
1051 | if (x == null || GEOMETRY_CLASS == null) { |
1052 | return false; |
1053 | } |
1054 | return GEOMETRY_CLASS.isAssignableFrom(x); |
1055 | } |
1056 | |
1057 | /** |
1058 | * Check whether a given object is a Geometry object. |
1059 | * |
1060 | * @param x the the object |
1061 | * @return true if it is a Geometry object |
1062 | */ |
1063 | public static boolean isGeometry(Object x) { |
1064 | if (x == null) { |
1065 | return false; |
1066 | } |
1067 | return isGeometryClass(x.getClass()); |
1068 | } |
1069 | |
1070 | /** |
1071 | * Get a data type object from a type name. |
1072 | * |
1073 | * @param s the type name |
1074 | * @return the data type object |
1075 | */ |
1076 | public static DataType getTypeByName(String s) { |
1077 | return TYPES_BY_NAME.get(s); |
1078 | } |
1079 | |
1080 | /** |
1081 | * Check if the given value type is a large object (BLOB or CLOB). |
1082 | * |
1083 | * @param type the value type |
1084 | * @return true if the value type is a lob type |
1085 | */ |
1086 | public static boolean isLargeObject(int type) { |
1087 | if (type == Value.BLOB || type == Value.CLOB) { |
1088 | return true; |
1089 | } |
1090 | return false; |
1091 | } |
1092 | |
1093 | /** |
1094 | * Check if the given value type is a String (VARCHAR,...). |
1095 | * |
1096 | * @param type the value type |
1097 | * @return true if the value type is a String type |
1098 | */ |
1099 | public static boolean isStringType(int type) { |
1100 | if (type == Value.STRING || type == Value.STRING_FIXED |
1101 | || type == Value.STRING_IGNORECASE) { |
1102 | return true; |
1103 | } |
1104 | return false; |
1105 | } |
1106 | |
1107 | /** |
1108 | * Check if the given value type supports the add operation. |
1109 | * |
1110 | * @param type the value type |
1111 | * @return true if add is supported |
1112 | */ |
1113 | public static boolean supportsAdd(int type) { |
1114 | switch (type) { |
1115 | case Value.BYTE: |
1116 | case Value.DECIMAL: |
1117 | case Value.DOUBLE: |
1118 | case Value.FLOAT: |
1119 | case Value.INT: |
1120 | case Value.LONG: |
1121 | case Value.SHORT: |
1122 | return true; |
1123 | default: |
1124 | return false; |
1125 | } |
1126 | } |
1127 | |
1128 | /** |
1129 | * Get the data type that will not overflow when calling 'add' 2 billion |
1130 | * times. |
1131 | * |
1132 | * @param type the value type |
1133 | * @return the data type that supports adding |
1134 | */ |
1135 | public static int getAddProofType(int type) { |
1136 | switch (type) { |
1137 | case Value.BYTE: |
1138 | return Value.LONG; |
1139 | case Value.FLOAT: |
1140 | return Value.DOUBLE; |
1141 | case Value.INT: |
1142 | return Value.LONG; |
1143 | case Value.LONG: |
1144 | return Value.DECIMAL; |
1145 | case Value.SHORT: |
1146 | return Value.LONG; |
1147 | default: |
1148 | return type; |
1149 | } |
1150 | } |
1151 | |
1152 | /** |
1153 | * Get the default value in the form of a Java object for the given Java |
1154 | * class. |
1155 | * |
1156 | * @param clazz the Java class |
1157 | * @return the default object |
1158 | */ |
1159 | public static Object getDefaultForPrimitiveType(Class<?> clazz) { |
1160 | if (clazz == Boolean.TYPE) { |
1161 | return Boolean.FALSE; |
1162 | } else if (clazz == Byte.TYPE) { |
1163 | return Byte.valueOf((byte) 0); |
1164 | } else if (clazz == Character.TYPE) { |
1165 | return Character.valueOf((char) 0); |
1166 | } else if (clazz == Short.TYPE) { |
1167 | return Short.valueOf((short) 0); |
1168 | } else if (clazz == Integer.TYPE) { |
1169 | return Integer.valueOf(0); |
1170 | } else if (clazz == Long.TYPE) { |
1171 | return Long.valueOf(0); |
1172 | } else if (clazz == Float.TYPE) { |
1173 | return Float.valueOf(0); |
1174 | } else if (clazz == Double.TYPE) { |
1175 | return Double.valueOf(0); |
1176 | } |
1177 | throw DbException.throwInternalError( |
1178 | "primitive=" + clazz.toString()); |
1179 | } |
1180 | |
1181 | /** |
1182 | * Convert a value to the specified class. |
1183 | * |
1184 | * @param conn the database connection |
1185 | * @param v the value |
1186 | * @param paramClass the target class |
1187 | * @return the converted object |
1188 | */ |
1189 | public static Object convertTo(JdbcConnection conn, Value v, |
1190 | Class<?> paramClass) { |
1191 | if (paramClass == Blob.class) { |
1192 | return new JdbcBlob(conn, v, 0); |
1193 | } else if (paramClass == Clob.class) { |
1194 | return new JdbcClob(conn, v, 0); |
1195 | } |
1196 | if (v.getType() == Value.JAVA_OBJECT) { |
1197 | Object o = SysProperties.serializeJavaObject ? JdbcUtils.deserialize(v.getBytes(), |
1198 | conn.getSession().getDataHandler()) : v.getObject(); |
1199 | if (paramClass.isAssignableFrom(o.getClass())) { |
1200 | return o; |
1201 | } |
1202 | } |
1203 | throw DbException.getUnsupportedException("converting to class " + paramClass.getName()); |
1204 | } |
1205 | |
1206 | } |