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.mvstore.db; |
7 | |
8 | import java.math.BigDecimal; |
9 | import java.math.BigInteger; |
10 | import java.nio.ByteBuffer; |
11 | import java.sql.ResultSet; |
12 | import java.sql.ResultSetMetaData; |
13 | import java.sql.SQLException; |
14 | import java.util.Arrays; |
15 | |
16 | import org.h2.api.ErrorCode; |
17 | import org.h2.message.DbException; |
18 | import org.h2.mvstore.DataUtils; |
19 | import org.h2.mvstore.WriteBuffer; |
20 | import org.h2.mvstore.rtree.SpatialDataType; |
21 | import org.h2.mvstore.rtree.SpatialKey; |
22 | import org.h2.mvstore.type.DataType; |
23 | import org.h2.result.SortOrder; |
24 | import org.h2.store.DataHandler; |
25 | import org.h2.tools.SimpleResultSet; |
26 | import org.h2.value.CompareMode; |
27 | import org.h2.value.Value; |
28 | import org.h2.value.ValueArray; |
29 | import org.h2.value.ValueBoolean; |
30 | import org.h2.value.ValueByte; |
31 | import org.h2.value.ValueBytes; |
32 | import org.h2.value.ValueDate; |
33 | import org.h2.value.ValueDecimal; |
34 | import org.h2.value.ValueDouble; |
35 | import org.h2.value.ValueFloat; |
36 | import org.h2.value.ValueGeometry; |
37 | import org.h2.value.ValueInt; |
38 | import org.h2.value.ValueJavaObject; |
39 | import org.h2.value.ValueLobDb; |
40 | import org.h2.value.ValueLong; |
41 | import org.h2.value.ValueNull; |
42 | import org.h2.value.ValueResultSet; |
43 | import org.h2.value.ValueShort; |
44 | import org.h2.value.ValueString; |
45 | import org.h2.value.ValueStringFixed; |
46 | import org.h2.value.ValueStringIgnoreCase; |
47 | import org.h2.value.ValueTime; |
48 | import org.h2.value.ValueTimestamp; |
49 | import org.h2.value.ValueUuid; |
50 | |
51 | /** |
52 | * A row type. |
53 | */ |
54 | public class ValueDataType implements DataType { |
55 | |
56 | private static final int INT_0_15 = 32; |
57 | private static final int LONG_0_7 = 48; |
58 | private static final int DECIMAL_0_1 = 56; |
59 | private static final int DECIMAL_SMALL_0 = 58; |
60 | private static final int DECIMAL_SMALL = 59; |
61 | private static final int DOUBLE_0_1 = 60; |
62 | private static final int FLOAT_0_1 = 62; |
63 | private static final int BOOLEAN_FALSE = 64; |
64 | private static final int BOOLEAN_TRUE = 65; |
65 | private static final int INT_NEG = 66; |
66 | private static final int LONG_NEG = 67; |
67 | private static final int STRING_0_31 = 68; |
68 | private static final int BYTES_0_31 = 100; |
69 | private static final int SPATIAL_KEY_2D = 132; |
70 | |
71 | final DataHandler handler; |
72 | final CompareMode compareMode; |
73 | final int[] sortTypes; |
74 | SpatialDataType spatialType; |
75 | |
76 | public ValueDataType(CompareMode compareMode, DataHandler handler, |
77 | int[] sortTypes) { |
78 | this.compareMode = compareMode; |
79 | this.handler = handler; |
80 | this.sortTypes = sortTypes; |
81 | } |
82 | |
83 | private SpatialDataType getSpatialDataType() { |
84 | if (spatialType == null) { |
85 | spatialType = new SpatialDataType(2); |
86 | } |
87 | return spatialType; |
88 | } |
89 | |
90 | @Override |
91 | public int compare(Object a, Object b) { |
92 | if (a == b) { |
93 | return 0; |
94 | } |
95 | if (a instanceof ValueArray && b instanceof ValueArray) { |
96 | Value[] ax = ((ValueArray) a).getList(); |
97 | Value[] bx = ((ValueArray) b).getList(); |
98 | int al = ax.length; |
99 | int bl = bx.length; |
100 | int len = Math.min(al, bl); |
101 | for (int i = 0; i < len; i++) { |
102 | int sortType = sortTypes[i]; |
103 | int comp = compareValues(ax[i], bx[i], sortType); |
104 | if (comp != 0) { |
105 | return comp; |
106 | } |
107 | } |
108 | if (len < al) { |
109 | return -1; |
110 | } else if (len < bl) { |
111 | return 1; |
112 | } |
113 | return 0; |
114 | } |
115 | return compareValues((Value) a, (Value) b, SortOrder.ASCENDING); |
116 | } |
117 | |
118 | private int compareValues(Value a, Value b, int sortType) { |
119 | if (a == b) { |
120 | return 0; |
121 | } |
122 | // null is never stored; |
123 | // comparison with null is used to retrieve all entries |
124 | // in which case null is always lower than all entries |
125 | // (even for descending ordered indexes) |
126 | if (a == null) { |
127 | return -1; |
128 | } else if (b == null) { |
129 | return 1; |
130 | } |
131 | boolean aNull = a == ValueNull.INSTANCE; |
132 | boolean bNull = b == ValueNull.INSTANCE; |
133 | if (aNull || bNull) { |
134 | return SortOrder.compareNull(aNull, sortType); |
135 | } |
136 | int comp = compareTypeSave(a, b); |
137 | if ((sortType & SortOrder.DESCENDING) != 0) { |
138 | comp = -comp; |
139 | } |
140 | return comp; |
141 | } |
142 | |
143 | private int compareTypeSave(Value a, Value b) { |
144 | if (a == b) { |
145 | return 0; |
146 | } |
147 | return a.compareTypeSave(b, compareMode); |
148 | } |
149 | |
150 | @Override |
151 | public int getMemory(Object obj) { |
152 | if (obj instanceof SpatialKey) { |
153 | return getSpatialDataType().getMemory(obj); |
154 | } |
155 | return getMemory((Value) obj); |
156 | } |
157 | |
158 | private static int getMemory(Value v) { |
159 | return v == null ? 0 : v.getMemory(); |
160 | } |
161 | |
162 | @Override |
163 | public void read(ByteBuffer buff, Object[] obj, int len, boolean key) { |
164 | for (int i = 0; i < len; i++) { |
165 | obj[i] = read(buff); |
166 | } |
167 | } |
168 | |
169 | @Override |
170 | public void write(WriteBuffer buff, Object[] obj, int len, boolean key) { |
171 | for (int i = 0; i < len; i++) { |
172 | write(buff, obj[i]); |
173 | } |
174 | } |
175 | |
176 | @Override |
177 | public Object read(ByteBuffer buff) { |
178 | return readValue(buff); |
179 | } |
180 | |
181 | @Override |
182 | public void write(WriteBuffer buff, Object obj) { |
183 | if (obj instanceof SpatialKey) { |
184 | buff.put((byte) SPATIAL_KEY_2D); |
185 | getSpatialDataType().write(buff, obj); |
186 | return; |
187 | } |
188 | Value x = (Value) obj; |
189 | writeValue(buff, x); |
190 | } |
191 | |
192 | private void writeValue(WriteBuffer buff, Value v) { |
193 | if (v == ValueNull.INSTANCE) { |
194 | buff.put((byte) 0); |
195 | return; |
196 | } |
197 | int type = v.getType(); |
198 | switch (type) { |
199 | case Value.BOOLEAN: |
200 | buff.put((byte) (v.getBoolean().booleanValue() ? |
201 | BOOLEAN_TRUE : BOOLEAN_FALSE)); |
202 | break; |
203 | case Value.BYTE: |
204 | buff.put((byte) type).put(v.getByte()); |
205 | break; |
206 | case Value.SHORT: |
207 | buff.put((byte) type).putShort(v.getShort()); |
208 | break; |
209 | case Value.INT: { |
210 | int x = v.getInt(); |
211 | if (x < 0) { |
212 | buff.put((byte) INT_NEG).putVarInt(-x); |
213 | } else if (x < 16) { |
214 | buff.put((byte) (INT_0_15 + x)); |
215 | } else { |
216 | buff.put((byte) type).putVarInt(x); |
217 | } |
218 | break; |
219 | } |
220 | case Value.LONG: { |
221 | long x = v.getLong(); |
222 | if (x < 0) { |
223 | buff.put((byte) LONG_NEG).putVarLong(-x); |
224 | } else if (x < 8) { |
225 | buff.put((byte) (LONG_0_7 + x)); |
226 | } else { |
227 | buff.put((byte) type).putVarLong(x); |
228 | } |
229 | break; |
230 | } |
231 | case Value.DECIMAL: { |
232 | BigDecimal x = v.getBigDecimal(); |
233 | if (BigDecimal.ZERO.equals(x)) { |
234 | buff.put((byte) DECIMAL_0_1); |
235 | } else if (BigDecimal.ONE.equals(x)) { |
236 | buff.put((byte) (DECIMAL_0_1 + 1)); |
237 | } else { |
238 | int scale = x.scale(); |
239 | BigInteger b = x.unscaledValue(); |
240 | int bits = b.bitLength(); |
241 | if (bits <= 63) { |
242 | if (scale == 0) { |
243 | buff.put((byte) DECIMAL_SMALL_0). |
244 | putVarLong(b.longValue()); |
245 | } else { |
246 | buff.put((byte) DECIMAL_SMALL). |
247 | putVarInt(scale). |
248 | putVarLong(b.longValue()); |
249 | } |
250 | } else { |
251 | byte[] bytes = b.toByteArray(); |
252 | buff.put((byte) type). |
253 | putVarInt(scale). |
254 | putVarInt(bytes.length). |
255 | put(bytes); |
256 | } |
257 | } |
258 | break; |
259 | } |
260 | case Value.TIME: { |
261 | ValueTime t = (ValueTime) v; |
262 | long nanos = t.getNanos(); |
263 | long millis = nanos / 1000000; |
264 | nanos -= millis * 1000000; |
265 | buff.put((byte) type). |
266 | putVarLong(millis). |
267 | putVarLong(nanos); |
268 | break; |
269 | } |
270 | case Value.DATE: { |
271 | long x = ((ValueDate) v).getDateValue(); |
272 | buff.put((byte) type).putVarLong(x); |
273 | break; |
274 | } |
275 | case Value.TIMESTAMP: { |
276 | ValueTimestamp ts = (ValueTimestamp) v; |
277 | long dateValue = ts.getDateValue(); |
278 | long nanos = ts.getTimeNanos(); |
279 | long millis = nanos / 1000000; |
280 | nanos -= millis * 1000000; |
281 | buff.put((byte) type). |
282 | putVarLong(dateValue). |
283 | putVarLong(millis). |
284 | putVarLong(nanos); |
285 | break; |
286 | } |
287 | case Value.JAVA_OBJECT: { |
288 | byte[] b = v.getBytesNoCopy(); |
289 | buff.put((byte) type). |
290 | putVarInt(b.length). |
291 | put(b); |
292 | break; |
293 | } |
294 | case Value.BYTES: { |
295 | byte[] b = v.getBytesNoCopy(); |
296 | int len = b.length; |
297 | if (len < 32) { |
298 | buff.put((byte) (BYTES_0_31 + len)). |
299 | put(b); |
300 | } else { |
301 | buff.put((byte) type). |
302 | putVarInt(b.length). |
303 | put(b); |
304 | } |
305 | break; |
306 | } |
307 | case Value.UUID: { |
308 | ValueUuid uuid = (ValueUuid) v; |
309 | buff.put((byte) type). |
310 | putLong(uuid.getHigh()). |
311 | putLong(uuid.getLow()); |
312 | break; |
313 | } |
314 | case Value.STRING: { |
315 | String s = v.getString(); |
316 | int len = s.length(); |
317 | if (len < 32) { |
318 | buff.put((byte) (STRING_0_31 + len)). |
319 | putStringData(s, len); |
320 | } else { |
321 | buff.put((byte) type); |
322 | writeString(buff, s); |
323 | } |
324 | break; |
325 | } |
326 | case Value.STRING_IGNORECASE: |
327 | case Value.STRING_FIXED: |
328 | buff.put((byte) type); |
329 | writeString(buff, v.getString()); |
330 | break; |
331 | case Value.DOUBLE: { |
332 | double x = v.getDouble(); |
333 | if (x == 1.0d) { |
334 | buff.put((byte) (DOUBLE_0_1 + 1)); |
335 | } else { |
336 | long d = Double.doubleToLongBits(x); |
337 | if (d == ValueDouble.ZERO_BITS) { |
338 | buff.put((byte) DOUBLE_0_1); |
339 | } else { |
340 | buff.put((byte) type). |
341 | putVarLong(Long.reverse(d)); |
342 | } |
343 | } |
344 | break; |
345 | } |
346 | case Value.FLOAT: { |
347 | float x = v.getFloat(); |
348 | if (x == 1.0f) { |
349 | buff.put((byte) (FLOAT_0_1 + 1)); |
350 | } else { |
351 | int f = Float.floatToIntBits(x); |
352 | if (f == ValueFloat.ZERO_BITS) { |
353 | buff.put((byte) FLOAT_0_1); |
354 | } else { |
355 | buff.put((byte) type). |
356 | putVarInt(Integer.reverse(f)); |
357 | } |
358 | } |
359 | break; |
360 | } |
361 | case Value.BLOB: |
362 | case Value.CLOB: { |
363 | buff.put((byte) type); |
364 | ValueLobDb lob = (ValueLobDb) v; |
365 | byte[] small = lob.getSmall(); |
366 | if (small == null) { |
367 | buff.putVarInt(-3). |
368 | putVarInt(lob.getTableId()). |
369 | putVarLong(lob.getLobId()). |
370 | putVarLong(lob.getPrecision()); |
371 | } else { |
372 | buff.putVarInt(small.length). |
373 | put(small); |
374 | } |
375 | break; |
376 | } |
377 | case Value.ARRAY: { |
378 | Value[] list = ((ValueArray) v).getList(); |
379 | buff.put((byte) type).putVarInt(list.length); |
380 | for (Value x : list) { |
381 | writeValue(buff, x); |
382 | } |
383 | break; |
384 | } |
385 | case Value.RESULT_SET: { |
386 | buff.put((byte) type); |
387 | try { |
388 | ResultSet rs = ((ValueResultSet) v).getResultSet(); |
389 | rs.beforeFirst(); |
390 | ResultSetMetaData meta = rs.getMetaData(); |
391 | int columnCount = meta.getColumnCount(); |
392 | buff.putVarInt(columnCount); |
393 | for (int i = 0; i < columnCount; i++) { |
394 | writeString(buff, meta.getColumnName(i + 1)); |
395 | buff.putVarInt(meta.getColumnType(i + 1)). |
396 | putVarInt(meta.getPrecision(i + 1)). |
397 | putVarInt(meta.getScale(i + 1)); |
398 | } |
399 | while (rs.next()) { |
400 | buff.put((byte) 1); |
401 | for (int i = 0; i < columnCount; i++) { |
402 | int t = org.h2.value.DataType. |
403 | getValueTypeFromResultSet(meta, i + 1); |
404 | Value val = org.h2.value.DataType.readValue( |
405 | null, rs, i + 1, t); |
406 | writeValue(buff, val); |
407 | } |
408 | } |
409 | buff.put((byte) 0); |
410 | rs.beforeFirst(); |
411 | } catch (SQLException e) { |
412 | throw DbException.convert(e); |
413 | } |
414 | break; |
415 | } |
416 | case Value.GEOMETRY: { |
417 | byte[] b = v.getBytes(); |
418 | int len = b.length; |
419 | buff.put((byte) type). |
420 | putVarInt(len). |
421 | put(b); |
422 | break; |
423 | } |
424 | default: |
425 | DbException.throwInternalError("type=" + v.getType()); |
426 | } |
427 | } |
428 | |
429 | private static void writeString(WriteBuffer buff, String s) { |
430 | int len = s.length(); |
431 | buff.putVarInt(len).putStringData(s, len); |
432 | } |
433 | |
434 | /** |
435 | * Read a value. |
436 | * |
437 | * @return the value |
438 | */ |
439 | private Object readValue(ByteBuffer buff) { |
440 | int type = buff.get() & 255; |
441 | switch (type) { |
442 | case Value.NULL: |
443 | return ValueNull.INSTANCE; |
444 | case BOOLEAN_TRUE: |
445 | return ValueBoolean.get(true); |
446 | case BOOLEAN_FALSE: |
447 | return ValueBoolean.get(false); |
448 | case INT_NEG: |
449 | return ValueInt.get(-readVarInt(buff)); |
450 | case Value.INT: |
451 | return ValueInt.get(readVarInt(buff)); |
452 | case LONG_NEG: |
453 | return ValueLong.get(-readVarLong(buff)); |
454 | case Value.LONG: |
455 | return ValueLong.get(readVarLong(buff)); |
456 | case Value.BYTE: |
457 | return ValueByte.get(buff.get()); |
458 | case Value.SHORT: |
459 | return ValueShort.get(buff.getShort()); |
460 | case DECIMAL_0_1: |
461 | return ValueDecimal.ZERO; |
462 | case DECIMAL_0_1 + 1: |
463 | return ValueDecimal.ONE; |
464 | case DECIMAL_SMALL_0: |
465 | return ValueDecimal.get(BigDecimal.valueOf( |
466 | readVarLong(buff))); |
467 | case DECIMAL_SMALL: { |
468 | int scale = readVarInt(buff); |
469 | return ValueDecimal.get(BigDecimal.valueOf( |
470 | readVarLong(buff), scale)); |
471 | } |
472 | case Value.DECIMAL: { |
473 | int scale = readVarInt(buff); |
474 | int len = readVarInt(buff); |
475 | byte[] buff2 = DataUtils.newBytes(len); |
476 | buff.get(buff2, 0, len); |
477 | BigInteger b = new BigInteger(buff2); |
478 | return ValueDecimal.get(new BigDecimal(b, scale)); |
479 | } |
480 | case Value.DATE: { |
481 | return ValueDate.fromDateValue(readVarLong(buff)); |
482 | } |
483 | case Value.TIME: { |
484 | long nanos = readVarLong(buff) * 1000000 + readVarLong(buff); |
485 | return ValueTime.fromNanos(nanos); |
486 | } |
487 | case Value.TIMESTAMP: { |
488 | long dateValue = readVarLong(buff); |
489 | long nanos = readVarLong(buff) * 1000000 + readVarLong(buff); |
490 | return ValueTimestamp.fromDateValueAndNanos(dateValue, nanos); |
491 | } |
492 | case Value.BYTES: { |
493 | int len = readVarInt(buff); |
494 | byte[] b = DataUtils.newBytes(len); |
495 | buff.get(b, 0, len); |
496 | return ValueBytes.getNoCopy(b); |
497 | } |
498 | case Value.JAVA_OBJECT: { |
499 | int len = readVarInt(buff); |
500 | byte[] b = DataUtils.newBytes(len); |
501 | buff.get(b, 0, len); |
502 | return ValueJavaObject.getNoCopy(null, b, handler); |
503 | } |
504 | case Value.UUID: |
505 | return ValueUuid.get(buff.getLong(), buff.getLong()); |
506 | case Value.STRING: |
507 | return ValueString.get(readString(buff)); |
508 | case Value.STRING_IGNORECASE: |
509 | return ValueStringIgnoreCase.get(readString(buff)); |
510 | case Value.STRING_FIXED: |
511 | return ValueStringFixed.get(readString(buff)); |
512 | case FLOAT_0_1: |
513 | return ValueFloat.get(0); |
514 | case FLOAT_0_1 + 1: |
515 | return ValueFloat.get(1); |
516 | case DOUBLE_0_1: |
517 | return ValueDouble.get(0); |
518 | case DOUBLE_0_1 + 1: |
519 | return ValueDouble.get(1); |
520 | case Value.DOUBLE: |
521 | return ValueDouble.get(Double.longBitsToDouble( |
522 | Long.reverse(readVarLong(buff)))); |
523 | case Value.FLOAT: |
524 | return ValueFloat.get(Float.intBitsToFloat( |
525 | Integer.reverse(readVarInt(buff)))); |
526 | case Value.BLOB: |
527 | case Value.CLOB: { |
528 | int smallLen = readVarInt(buff); |
529 | if (smallLen >= 0) { |
530 | byte[] small = DataUtils.newBytes(smallLen); |
531 | buff.get(small, 0, smallLen); |
532 | return ValueLobDb.createSmallLob(type, small); |
533 | } else if (smallLen == -3) { |
534 | int tableId = readVarInt(buff); |
535 | long lobId = readVarLong(buff); |
536 | long precision = readVarLong(buff); |
537 | ValueLobDb lob = ValueLobDb.create(type, |
538 | handler, tableId, lobId, null, precision); |
539 | return lob; |
540 | } else { |
541 | throw DbException.get(ErrorCode.FILE_CORRUPTED_1, |
542 | "lob type: " + smallLen); |
543 | } |
544 | } |
545 | case Value.ARRAY: { |
546 | int len = readVarInt(buff); |
547 | Value[] list = new Value[len]; |
548 | for (int i = 0; i < len; i++) { |
549 | list[i] = (Value) readValue(buff); |
550 | } |
551 | return ValueArray.get(list); |
552 | } |
553 | case Value.RESULT_SET: { |
554 | SimpleResultSet rs = new SimpleResultSet(); |
555 | rs.setAutoClose(false); |
556 | int columns = readVarInt(buff); |
557 | for (int i = 0; i < columns; i++) { |
558 | rs.addColumn(readString(buff), |
559 | readVarInt(buff), |
560 | readVarInt(buff), |
561 | readVarInt(buff)); |
562 | } |
563 | while (true) { |
564 | if (buff.get() == 0) { |
565 | break; |
566 | } |
567 | Object[] o = new Object[columns]; |
568 | for (int i = 0; i < columns; i++) { |
569 | o[i] = ((Value) readValue(buff)).getObject(); |
570 | } |
571 | rs.addRow(o); |
572 | } |
573 | return ValueResultSet.get(rs); |
574 | } |
575 | case Value.GEOMETRY: { |
576 | int len = readVarInt(buff); |
577 | byte[] b = DataUtils.newBytes(len); |
578 | buff.get(b, 0, len); |
579 | return ValueGeometry.get(b); |
580 | } |
581 | case SPATIAL_KEY_2D: |
582 | return getSpatialDataType().read(buff); |
583 | default: |
584 | if (type >= INT_0_15 && type < INT_0_15 + 16) { |
585 | return ValueInt.get(type - INT_0_15); |
586 | } else if (type >= LONG_0_7 && type < LONG_0_7 + 8) { |
587 | return ValueLong.get(type - LONG_0_7); |
588 | } else if (type >= BYTES_0_31 && type < BYTES_0_31 + 32) { |
589 | int len = type - BYTES_0_31; |
590 | byte[] b = DataUtils.newBytes(len); |
591 | buff.get(b, 0, len); |
592 | return ValueBytes.getNoCopy(b); |
593 | } else if (type >= STRING_0_31 && type < STRING_0_31 + 32) { |
594 | return ValueString.get(readString(buff, type - STRING_0_31)); |
595 | } |
596 | throw DbException.get(ErrorCode.FILE_CORRUPTED_1, "type: " + type); |
597 | } |
598 | } |
599 | |
600 | private static int readVarInt(ByteBuffer buff) { |
601 | return DataUtils.readVarInt(buff); |
602 | } |
603 | |
604 | private static long readVarLong(ByteBuffer buff) { |
605 | return DataUtils.readVarLong(buff); |
606 | } |
607 | |
608 | private static String readString(ByteBuffer buff, int len) { |
609 | return DataUtils.readString(buff, len); |
610 | } |
611 | |
612 | private static String readString(ByteBuffer buff) { |
613 | int len = readVarInt(buff); |
614 | return DataUtils.readString(buff, len); |
615 | } |
616 | |
617 | @Override |
618 | public int hashCode() { |
619 | return compareMode.hashCode() ^ Arrays.hashCode(sortTypes); |
620 | } |
621 | |
622 | @Override |
623 | public boolean equals(Object obj) { |
624 | if (obj == this) { |
625 | return true; |
626 | } else if (!(obj instanceof ValueDataType)) { |
627 | return false; |
628 | } |
629 | ValueDataType v = (ValueDataType) obj; |
630 | if (!compareMode.equals(v.compareMode)) { |
631 | return false; |
632 | } |
633 | return Arrays.equals(sortTypes, v.sortTypes); |
634 | } |
635 | |
636 | } |