File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\value\ValueLobDb.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.value;
8
9 import java.io.BufferedInputStream;
10 import java.io.ByteArrayInputStream;
11 import java.io.IOException;
12 import java.io.InputStream;
13 import java.io.Reader;
14 import java.sql.PreparedStatement;
15 import java.sql.SQLException;
16 import org.h2.constant.SysProperties;
17 import org.h2.engine.Constants;
18 import org.h2.message.DbException;
19 import org.h2.store.DataHandler;
20 import org.h2.store.FileStore;
21 import org.h2.store.FileStoreInputStream;
22 import org.h2.store.FileStoreOutputStream;
23 import org.h2.store.LobStorage;
24 import org.h2.store.fs.FileUtils;
25 import org.h2.util.IOUtils;
26 import org.h2.util.MathUtils;
27 import org.h2.util.StringUtils;
28 import org.h2.util.Utils;
29
30 /**
31 * An alternate LOB implementation.
32 */
33 public class ValueLobDb extends Value implements Value.ValueClob, Value.ValueBlob {
34
35 private final int type;
36 private long precision;
37 private int tableId;
38 private int hash;
39
40 private LobStorage lobStorage;
41 private final long lobId;
42 private final byte[] hmac;
43
44 private byte[] small;
45
46 private DataHandler handler;
47 private FileStore tempFile;
48 private String fileName;
49
50 private ValueLobDb(int type, LobStorage lobStorage, int tableId, long lobId, byte[] hmac, long precision) {
51 this.type = type;
52 this.lobStorage = lobStorage;
53 this.tableId = tableId;
54 this.lobId = lobId;
55 this.hmac = hmac;
56 this.precision = precision;
57 }
58
59 private ValueLobDb(int type, byte[] small, long precision) {
60 this.type = type;
61 this.small = small;
62 this.precision = precision;
63 this.lobId = 0;
64 this.hmac = null;
65 }
66
67 /**
68 * Create a LOB value.
69 *
70 * @param type the type
71 * @param lobStorage the storage
72 * @param tableId the table id
73 * @param id the lob id
74 * @param hmac the message authentication code
75 * @param precision the precision (number of bytes / characters)
76 * @return the value
77 */
78 public static ValueLobDb create(int type, LobStorage lobStorage,
79 int tableId, long id, byte[] hmac, long precision) {
80 return new ValueLobDb(type, lobStorage, tableId, id, hmac, precision);
81 }
82
83 /**
84 * Create a small lob using the given byte array.
85 *
86 * @param type the type (Value.BLOB or CLOB)
87 * @param small the byte array
88 * @param precision the precision
89 * @return the lob value
90 */
91 public static ValueLobDb createSmallLob(int type, byte[] small, long precision) {
92 return new ValueLobDb(type, small, precision);
93 }
94
95 /**
96 * Convert a lob to another data type. The data is fully read in memory
97 * except when converting to BLOB or CLOB.
98 *
99 * @param t the new type
100 * @return the converted value
101 */
102 public Value convertTo(int t) {
103 if (t == type) {
104 return this;
105 } else if (t == Value.CLOB) {
106 if (lobStorage != null) {
107 Value copy = lobStorage.createClob(getReader(), -1);
108 return copy;
109 } else if (small != null) {
110 return LobStorage.createSmallLob(t, small);
111 }
112 } else if (t == Value.BLOB) {
113 if (lobStorage != null) {
114 Value copy = lobStorage.createBlob(getInputStream(), -1);
115 return copy;
116 } else if (small != null) {
117 return LobStorage.createSmallLob(t, small);
118 }
119 }
120 return super.convertTo(t);
121 }
122
123 public boolean isLinked() {
124 return tableId != LobStorage.TABLE_ID_SESSION_VARIABLE && small == null;
125 }
126
127 public boolean isStored() {
128 return small == null && fileName == null;
129 }
130
131 public void close() {
132 if (fileName != null) {
133 if (tempFile != null) {
134 tempFile.stopAutoDelete();
135 }
136 // synchronize on the database, to avoid concurrent temp file creation /
137 // deletion / backup
138 synchronized (handler.getLobSyncObject()) {
139 FileUtils.delete(fileName);
140 }
141 }
142 if (lobStorage != null) {
143 lobStorage.removeLob(lobId);
144 lobStorage = null;
145 }
146 }
147
148 public void unlink() {
149 if (small == null && tableId != LobStorage.TABLE_ID_SESSION_VARIABLE) {
150 lobStorage.setTable(lobId, LobStorage.TABLE_ID_SESSION_VARIABLE);
151 tableId = LobStorage.TABLE_ID_SESSION_VARIABLE;
152 }
153 }
154
155 public Value link(DataHandler h, int tabId) {
156 if (small == null) {
157 if (tableId == LobStorage.TABLE_TEMP) {
158 lobStorage.setTable(lobId, tabId);
159 this.tableId = tabId;
160 } else {
161 return lobStorage.copyLob(type, lobId, tabId, getPrecision());
162 }
163 } else if (small.length > h.getMaxLengthInplaceLob()) {
164 LobStorage s = h.getLobStorage();
165 Value v;
166 if (type == Value.BLOB) {
167 v = s.createBlob(getInputStream(), getPrecision());
168 } else {
169 v = s.createClob(getReader(), getPrecision());
170 }
171 return v.link(h, tabId);
172 }
173 return this;
174 }
175
176 /**
177 * Get the current table id of this lob.
178 *
179 * @return the table id
180 */
181 public int getTableId() {
182 return tableId;
183 }
184
185 public int getType() {
186 return type;
187 }
188
189 public long getPrecision() {
190 return precision;
191 }
192
193 public String getString() {
194 int len = precision > Integer.MAX_VALUE || precision == 0 ? Integer.MAX_VALUE : (int) precision;
195 try {
196 if (type == Value.CLOB) {
197 if (small != null) {
198 return StringUtils.utf8Decode(small);
199 }
200 return IOUtils.readStringAndClose(getReader(), len);
201 }
202 byte[] buff;
203 if (small != null) {
204 buff = small;
205 } else {
206 buff = IOUtils.readBytesAndClose(getInputStream(), len);
207 }
208 return StringUtils.convertBytesToHex(buff);
209 } catch (IOException e) {
210 throw DbException.convertIOException(e, toString());
211 }
212 }
213
214 public byte[] getBytes() {
215 if (type == CLOB) {
216 // convert hex to string
217 return super.getBytes();
218 }
219 byte[] data = getBytesNoCopy();
220 return Utils.cloneByteArray(data);
221 }
222
223 public byte[] getBytesNoCopy() {
224 if (type == CLOB) {
225 // convert hex to string
226 return super.getBytesNoCopy();
227 }
228 if (small != null) {
229 return small;
230 }
231 try {
232 return IOUtils.readBytesAndClose(getInputStream(), Integer.MAX_VALUE);
233 } catch (IOException e) {
234 throw DbException.convertIOException(e, toString());
235 }
236 }
237
238 public int hashCode() {
239 if (hash == 0) {
240 if (precision > 4096) {
241 // TODO: should calculate the hash code when saving, and store
242 // it in the database file
243 return (int) (precision ^ (precision >>> 32));
244 }
245 if (type == CLOB) {
246 hash = getString().hashCode();
247 } else {
248 hash = Utils.getByteArrayHash(getBytes());
249 }
250 }
251 return hash;
252 }
253
254 protected int compareSecure(Value v, CompareMode mode) {
255 if (v instanceof ValueLobDb) {
256 ValueLobDb v2 = (ValueLobDb) v;
257 if (v == this) {
258 return 0;
259 }
260 if (lobId == v2.lobId && small == null && v2.small == null) {
261 return 0;
262 }
263 }
264 if (type == Value.CLOB) {
265 return Integer.signum(getString().compareTo(v.getString()));
266 }
267 byte[] v2 = v.getBytesNoCopy();
268 return Utils.compareNotNull(getBytes(), v2);
269 }
270
271 public Object getObject() {
272 if (type == Value.CLOB) {
273 return getReader();
274 }
275 return getInputStream();
276 }
277
278 public Reader getReader() {
279 return IOUtils.getBufferedReader(getInputStream());
280 }
281
282 public InputStream getInputStream() {
283 if (small != null) {
284 return new ByteArrayInputStream(small);
285 } else if (fileName != null) {
286 FileStore store = handler.openFile(fileName, "r", true);
287 boolean alwaysClose = SysProperties.lobCloseBetweenReads;
288 return new BufferedInputStream(new FileStoreInputStream(store, handler, false, alwaysClose),
289 Constants.IO_BUFFER_SIZE);
290 }
291 long byteCount = (type == Value.BLOB) ? precision : -1;
292 try {
293 return lobStorage.getInputStream(lobId, hmac, byteCount);
294 } catch (IOException e) {
295 throw DbException.convertIOException(e, toString());
296 }
297 }
298
299 public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
300 long p = getPrecision();
301 if (p > Integer.MAX_VALUE || p <= 0) {
302 p = -1;
303 }
304 if (type == Value.BLOB) {
305 prep.setBinaryStream(parameterIndex, getInputStream(), (int) p);
306 } else {
307 prep.setCharacterStream(parameterIndex, getReader(), (int) p);
308 }
309 }
310
311 public String getSQL() {
312 String s;
313 if (type == Value.CLOB) {
314 s = getString();
315 return StringUtils.quoteStringSQL(s);
316 }
317 byte[] buff = getBytes();
318 s = StringUtils.convertBytesToHex(buff);
319 return "X'" + s + "'";
320 }
321
322 public String getTraceSQL() {
323 if (small != null && getPrecision() <= SysProperties.MAX_TRACE_DATA_LENGTH) {
324 return getSQL();
325 }
326 StringBuilder buff = new StringBuilder();
327 if (type == Value.CLOB) {
328 buff.append("SPACE(").append(getPrecision());
329 } else {
330 buff.append("CAST(REPEAT('00', ").append(getPrecision()).append(") AS BINARY");
331 }
332 buff.append(" /* table: ").append(tableId).append(" id: ").append(lobId).append(" */)");
333 return buff.toString();
334 }
335
336 /**
337 * Get the data if this a small lob value.
338 *
339 * @return the data
340 */
341 public byte[] getSmall() {
342 return small;
343 }
344
345 public int getDisplaySize() {
346 return MathUtils.convertLongToInt(getPrecision());
347 }
348
349 public boolean equals(Object other) {
350 return other instanceof ValueLobDb && compareSecure((Value) other, null) == 0;
351 }
352
353 public int getMemory() {
354 if (small != null) {
355 return small.length + 104;
356 }
357 return 140;
358 }
359
360 /**
361 * Create an independent copy of this temporary value.
362 * The file will not be deleted automatically.
363 *
364 * @return the value
365 */
366 public ValueLobDb copyToTemp() {
367 return this;
368 }
369
370 public long getLobId() {
371 return lobId;
372 }
373
374 public void setPrecision(long precision) {
375 this.precision = precision;
376 }
377
378 public String toString() {
379 return "lob: " + fileName + " table: " + tableId + " id: " + lobId;
380 }
381
382 /**
383 * Create a temporary CLOB value from a stream.
384 *
385 * @param in the reader
386 * @param length the number of characters to read, or -1 for no limit
387 * @param handler the data handler
388 * @return the lob value
389 */
390 public static ValueLobDb createTempClob(Reader in, long length, DataHandler handler) {
391 try {
392 boolean compress = handler.getLobCompressionAlgorithm(Value.CLOB) != null;
393 long remaining = Long.MAX_VALUE;
394 if (length >= 0 && length < remaining) {
395 remaining = length;
396 }
397 int len = getBufferSize(handler, compress, remaining);
398 char[] buff;
399 if (len >= Integer.MAX_VALUE) {
400 String data = IOUtils.readStringAndClose(in, -1);
401 buff = data.toCharArray();
402 len = buff.length;
403 } else {
404 buff = new char[len];
405 len = IOUtils.readFully(in, buff, len);
406 len = len < 0 ? 0 : len;
407 }
408 if (len <= handler.getMaxLengthInplaceLob()) {
409 byte[] small = StringUtils.utf8Encode(new String(buff, 0, len));
410 return ValueLobDb.createSmallLob(Value.CLOB, small, len);
411 }
412 ValueLobDb lob = new ValueLobDb(Value.CLOB, null, 0);
413 lob.createTempFromReader(buff, len, in, remaining, handler);
414 return lob;
415 } catch (IOException e) {
416 throw DbException.convertIOException(e, null);
417 }
418 }
419
420 /**
421 * Create a temporary BLOB value from a stream.
422 *
423 * @param in the input stream
424 * @param length the number of characters to read, or -1 for no limit
425 * @param handler the data handler
426 * @return the lob value
427 */
428 public static ValueLobDb createTempBlob(InputStream in, long length, DataHandler handler) {
429 try {
430 long remaining = Long.MAX_VALUE;
431 boolean compress = handler.getLobCompressionAlgorithm(Value.BLOB) != null;
432 if (length >= 0 && length < remaining) {
433 remaining = length;
434 }
435 int len = getBufferSize(handler, compress, remaining);
436 byte[] buff;
437 if (len >= Integer.MAX_VALUE) {
438 buff = IOUtils.readBytesAndClose(in, -1);
439 len = buff.length;
440 } else {
441 buff = Utils.newBytes(len);
442 len = IOUtils.readFully(in, buff, 0, len);
443 }
444 if (len <= handler.getMaxLengthInplaceLob()) {
445 byte[] small = Utils.newBytes(len);
446 System.arraycopy(buff, 0, small, 0, len);
447 return ValueLobDb.createSmallLob(Value.BLOB, small, small.length);
448 }
449 ValueLobDb lob = new ValueLobDb(Value.BLOB, null, 0);
450 lob.createTempFromStream(buff, len, in, remaining, handler);
451 return lob;
452 } catch (IOException e) {
453 throw DbException.convertIOException(e, null);
454 }
455 }
456
457 private void createTempFromReader(char[] buff, int len, Reader in, long remaining, DataHandler h) {
458 try {
459 FileStoreOutputStream out = initTemp(h);
460 try {
461 while (true) {
462 precision += len;
463 byte[] b = StringUtils.utf8Encode(new String(buff, 0, len));
464 out.write(b, 0, b.length);
465 remaining -= len;
466 if (remaining <= 0) {
467 break;
468 }
469 len = getBufferSize(h, false, remaining);
470 len = IOUtils.readFully(in, buff, len);
471 if (len <= 0) {
472 break;
473 }
474 }
475 } finally {
476 out.close();
477 }
478 } catch (IOException e) {
479 throw DbException.convertIOException(e, null);
480 }
481 }
482
483 private void createTempFromStream(byte[] buff, int len, InputStream in, long remaining, DataHandler h) {
484 try {
485 FileStoreOutputStream out = initTemp(h);
486 boolean compress = h.getLobCompressionAlgorithm(Value.BLOB) != null;
487 try {
488 while (true) {
489 precision += len;
490 out.write(buff, 0, len);
491 remaining -= len;
492 if (remaining <= 0) {
493 break;
494 }
495 len = getBufferSize(h, compress, remaining);
496 len = IOUtils.readFully(in, buff, 0, len);
497 if (len <= 0) {
498 break;
499 }
500 }
501 } finally {
502 out.close();
503 }
504 } catch (IOException e) {
505 throw DbException.convertIOException(e, null);
506 }
507 }
508
509 private FileStoreOutputStream initTemp(DataHandler h) {
510 this.precision = 0;
511 this.handler = h;
512 this.lobStorage = h.getLobStorage();
513 this.small = null;
514 try {
515 String path = h.getDatabasePath();
516 if (path.length() == 0) {
517 path = SysProperties.PREFIX_TEMP_FILE;
518 }
519 fileName = FileUtils.createTempFile(path, Constants.SUFFIX_TEMP_FILE, true, true);
520 } catch (IOException e) {
521 throw DbException.convertIOException(e, null);
522 }
523 tempFile = h.openFile(fileName, "rw", false);
524 tempFile.autoDelete();
525 FileStoreOutputStream out = new FileStoreOutputStream(tempFile, null, null);
526 return out;
527 }
528
529 private static int getBufferSize(DataHandler handler, boolean compress, long remaining) {
530 if (remaining < 0 || remaining > Integer.MAX_VALUE) {
531 remaining = Integer.MAX_VALUE;
532 }
533 long inplace = handler.getMaxLengthInplaceLob();
534 if (inplace >= Integer.MAX_VALUE) {
535 inplace = remaining;
536 }
537 long m = compress ? Constants.IO_BUFFER_SIZE_COMPRESS : Constants.IO_BUFFER_SIZE;
538 if (m < remaining && m <= inplace) {
539 m = Math.min(remaining, inplace + 1);
540 // the buffer size must be bigger than the inplace lob, otherwise we can't
541 // know if it must be stored in-place or not
542 m = MathUtils.roundUpLong(m, Constants.IO_BUFFER_SIZE);
543 }
544 m = Math.min(remaining, m);
545 m = MathUtils.convertLongToInt(m);
546 if (m < 0) {
547 m = Integer.MAX_VALUE;
548 }
549 return (int) m;
550 }
551
552 public Value convertPrecision(long precision, boolean force) {
553 if (this.precision <= precision) {
554 return this;
555 }
556 ValueLob lob;
557 if (type == CLOB) {
558 lob = ValueLob.createClob(getReader(), precision, handler);
559 } else {
560 lob = ValueLob.createBlob(getInputStream(), precision, handler);
561 }
562 return lob;
563 }
564
565 }
566