File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\value\ValueLob.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.File;
12 import java.io.IOException;
13 import java.io.InputStream;
14 import java.io.Reader;
15 import java.sql.PreparedStatement;
16 import java.sql.SQLException;
17 import org.h2.constant.SysProperties;
18 import org.h2.engine.Constants;
19 import org.h2.message.DbException;
20 import org.h2.store.DataHandler;
21 import org.h2.store.FileStore;
22 import org.h2.store.FileStoreInputStream;
23 import org.h2.store.FileStoreOutputStream;
24 import org.h2.store.fs.FileUtils;
25 import org.h2.util.IOUtils;
26 import org.h2.util.MathUtils;
27 import org.h2.util.SmallLRUCache;
28 import org.h2.util.StringUtils;
29 import org.h2.util.Utils;
30
31 /**
32 * Implementation of the BLOB and CLOB data types. Small objects are kept in
33 * memory and stored in the record.
34 *
35 * Large objects are stored in their own files. When large objects are set in a
36 * prepared statement, they are first stored as 'temporary' files. Later, when
37 * they are used in a record, and when the record is stored, the lob files are
38 * linked: the file is renamed using the file format (tableId).(objectId). There
39 * is one exception: large variables are stored in the file (-1).(objectId).
40 *
41 * When lobs are deleted, they are first renamed to a temp file, and if the
42 * delete operation is committed the file is deleted.
43 *
44 * Data compression is supported.
45 */
46 public class ValueLob extends Value {
47
48 /**
49 * This counter is used to calculate the next directory to store lobs. It is
50 * better than using a random number because less directories are created.
51 */
52 private static int dirCounter;
53
54 private final int type;
55 private long precision;
56 private DataHandler handler;
57 private int tableId;
58 private int objectId;
59 private String fileName;
60 private boolean linked;
61 private byte[] small;
62 private int hash;
63 private boolean compression;
64 private FileStore tempFile;
65
66 private ValueLob(int type, DataHandler handler, String fileName, int tableId, int objectId, boolean linked,
67 long precision, boolean compression) {
68 this.type = type;
69 this.handler = handler;
70 this.fileName = fileName;
71 this.tableId = tableId;
72 this.objectId = objectId;
73 this.linked = linked;
74 this.precision = precision;
75 this.compression = compression;
76 }
77
78 private ValueLob(int type, byte[] small) {
79 this.type = type;
80 this.small = small;
81 if (small != null) {
82 if (type == Value.BLOB) {
83 this.precision = small.length;
84 } else {
85 this.precision = getString().length();
86 }
87 }
88 }
89
90 private static ValueLob copy(ValueLob lob) {
91 ValueLob copy = new ValueLob(lob.type, lob.handler, lob.fileName,
92 lob.tableId, lob.objectId, lob.linked, lob.precision, lob.compression);
93 copy.small = lob.small;
94 copy.hash = lob.hash;
95 return copy;
96 }
97
98 /**
99 * Create a small lob using the given byte array.
100 *
101 * @param type the type (Value.BLOB or CLOB)
102 * @param small the byte array
103 * @return the lob value
104 */
105 public static ValueLob createSmallLob(int type, byte[] small) {
106 return new ValueLob(type, small);
107 }
108
109 private static String getFileName(DataHandler handler, int tableId, int objectId) {
110 if (SysProperties.CHECK && tableId == 0 && objectId == 0) {
111 DbException.throwInternalError("0 LOB");
112 }
113 String table = tableId < 0 ? ".temp" : ".t" + tableId;
114 return getFileNamePrefix(handler.getDatabasePath(), objectId) + table + Constants.SUFFIX_LOB_FILE;
115 }
116
117 /**
118 * Create a LOB value with the given parameters.
119 *
120 * @param type the data type
121 * @param handler the file handler
122 * @param tableId the table object id
123 * @param objectId the object id
124 * @param precision the precision (length in elements)
125 * @param compression if compression is used
126 * @return the value object
127 */
128 public static ValueLob open(int type, DataHandler handler,
129 int tableId, int objectId, long precision, boolean compression) {
130 String fileName = getFileName(handler, tableId, objectId);
131 return new ValueLob(type, handler, fileName, tableId, objectId, true, precision, compression);
132 }
133
134 /**
135 * Create a CLOB value from a stream.
136 *
137 * @param in the reader
138 * @param length the number of characters to read, or -1 for no limit
139 * @param handler the data handler
140 * @return the lob value
141 */
142 public static ValueLob createClob(Reader in, long length, DataHandler handler) {
143 try {
144 if (handler == null) {
145 String s = IOUtils.readStringAndClose(in, (int) length);
146 return createSmallLob(Value.CLOB, StringUtils.utf8Encode(s));
147 }
148 boolean compress = handler.getLobCompressionAlgorithm(Value.CLOB) != null;
149 long remaining = Long.MAX_VALUE;
150 if (length >= 0 && length < remaining) {
151 remaining = length;
152 }
153 int len = getBufferSize(handler, compress, remaining);
154 char[] buff;
155 if (len >= Integer.MAX_VALUE) {
156 String data = IOUtils.readStringAndClose(in, -1);
157 buff = data.toCharArray();
158 len = buff.length;
159 } else {
160 buff = new char[len];
161 len = IOUtils.readFully(in, buff, len);
162 len = len < 0 ? 0 : len;
163 }
164 if (len <= handler.getMaxLengthInplaceLob()) {
165 byte[] small = StringUtils.utf8Encode(new String(buff, 0, len));
166 return ValueLob.createSmallLob(Value.CLOB, small);
167 }
168 ValueLob lob = new ValueLob(Value.CLOB, null);
169 lob.createFromReader(buff, len, in, remaining, handler);
170 return lob;
171 } catch (IOException e) {
172 throw DbException.convertIOException(e, null);
173 }
174 }
175
176 private static int getBufferSize(DataHandler handler, boolean compress, long remaining) {
177 if (remaining < 0 || remaining > Integer.MAX_VALUE) {
178 remaining = Integer.MAX_VALUE;
179 }
180 long inplace = handler.getMaxLengthInplaceLob();
181 if (inplace >= Integer.MAX_VALUE) {
182 inplace = remaining;
183 }
184 long m = compress ? Constants.IO_BUFFER_SIZE_COMPRESS : Constants.IO_BUFFER_SIZE;
185 if (m < remaining && m <= inplace) {
186 m = Math.min(remaining, inplace + 1);
187 // the buffer size must be bigger than the inplace lob, otherwise we can't
188 // know if it must be stored in-place or not
189 m = MathUtils.roundUpLong(m, Constants.IO_BUFFER_SIZE);
190 }
191 m = Math.min(remaining, m);
192 m = MathUtils.convertLongToInt(m);
193 if (m < 0) {
194 m = Integer.MAX_VALUE;
195 }
196 return (int) m;
197 }
198
199 private void createFromReader(char[] buff, int len, Reader in, long remaining, DataHandler h) {
200 try {
201 FileStoreOutputStream out = initLarge(h);
202 boolean compress = h.getLobCompressionAlgorithm(Value.CLOB) != null;
203 try {
204 while (true) {
205 precision += len;
206 byte[] b = StringUtils.utf8Encode(new String(buff, 0, len));
207 out.write(b, 0, b.length);
208 remaining -= len;
209 if (remaining <= 0) {
210 break;
211 }
212 len = getBufferSize(h, compress, remaining);
213 len = IOUtils.readFully(in, buff, len);
214 if (len <= 0) {
215 break;
216 }
217 }
218 } finally {
219 out.close();
220 }
221 } catch (IOException e) {
222 throw DbException.convertIOException(e, null);
223 }
224 }
225
226 private static String getFileNamePrefix(String path, int objectId) {
227 String name;
228 int f = objectId % SysProperties.LOB_FILES_PER_DIRECTORY;
229 if (f > 0) {
230 name = SysProperties.FILE_SEPARATOR + objectId;
231 } else {
232 name = "";
233 }
234 objectId /= SysProperties.LOB_FILES_PER_DIRECTORY;
235 while (objectId > 0) {
236 f = objectId % SysProperties.LOB_FILES_PER_DIRECTORY;
237 name = SysProperties.FILE_SEPARATOR + f + Constants.SUFFIX_LOBS_DIRECTORY + name;
238 objectId /= SysProperties.LOB_FILES_PER_DIRECTORY;
239 }
240 name = FileUtils.toRealPath(path + Constants.SUFFIX_LOBS_DIRECTORY + name);
241 return name;
242 }
243
244 private static int getNewObjectId(DataHandler h) {
245 String path = h.getDatabasePath();
246 if ((path != null) && (path.length() == 0)) {
247 path = new File(Utils.getProperty("java.io.tmpdir", "."), SysProperties.PREFIX_TEMP_FILE).getAbsolutePath();
248 }
249 int newId = 0;
250 int lobsPerDir = SysProperties.LOB_FILES_PER_DIRECTORY;
251 while (true) {
252 String dir = getFileNamePrefix(path, newId);
253 String[] list = getFileList(h, dir);
254 int fileCount = 0;
255 boolean[] used = new boolean[lobsPerDir];
256 for (String name : list) {
257 if (name.endsWith(Constants.SUFFIX_DB_FILE)) {
258 name = FileUtils.getName(name);
259 String n = name.substring(0, name.indexOf('.'));
260 int id;
261 try {
262 id = Integer.parseInt(n);
263 } catch (NumberFormatException e) {
264 id = -1;
265 }
266 if (id > 0) {
267 fileCount++;
268 used[id % lobsPerDir] = true;
269 }
270 }
271 }
272 int fileId = -1;
273 if (fileCount < lobsPerDir) {
274 for (int i = 1; i < lobsPerDir; i++) {
275 if (!used[i]) {
276 fileId = i;
277 break;
278 }
279 }
280 }
281 if (fileId > 0) {
282 newId += fileId;
283 invalidateFileList(h, dir);
284 break;
285 }
286 if (newId > Integer.MAX_VALUE / lobsPerDir) {
287 // this directory path is full: start from zero
288 newId = 0;
289 dirCounter = MathUtils.randomInt(lobsPerDir - 1) * lobsPerDir;
290 } else {
291 // calculate the directory
292 // start with 1 (otherwise we don't know the number of directories)
293 // it doesn't really matter what directory is used, it might as well be random
294 // (but that would generate more directories):
295 // int dirId = RandomUtils.nextInt(lobsPerDir - 1) + 1;
296 int dirId = (dirCounter++ / (lobsPerDir - 1)) + 1;
297 newId = newId * lobsPerDir;
298 newId += dirId * lobsPerDir;
299 }
300 }
301 return newId;
302 }
303
304 /**
305 * Reset the directory counter as if the process was stopped. This method is
306 * for debugging only (to simulate stopping a process).
307 */
308 public static void resetDirCounter() {
309 dirCounter = 0;
310 }
311
312 private static void invalidateFileList(DataHandler h, String dir) {
313 SmallLRUCache<String, String[]> cache = h.getLobFileListCache();
314 if (cache != null) {
315 synchronized (cache) {
316 cache.remove(dir);
317 }
318 }
319 }
320
321 private static String[] getFileList(DataHandler h, String dir) {
322 SmallLRUCache<String, String[]> cache = h.getLobFileListCache();
323 String[] list;
324 if (cache == null) {
325 list = FileUtils.newDirectoryStream(dir).toArray(new String[0]);
326 } else {
327 synchronized (cache) {
328 list = cache.get(dir);
329 if (list == null) {
330 list = FileUtils.newDirectoryStream(dir).toArray(new String[0]);
331 cache.put(dir, list);
332 }
333 }
334 }
335 return list;
336 }
337
338 /**
339 * Create a BLOB value from a stream.
340 *
341 * @param in the input stream
342 * @param length the number of characters to read, or -1 for no limit
343 * @param handler the data handler
344 * @return the lob value
345 */
346 public static ValueLob createBlob(InputStream in, long length, DataHandler handler) {
347 try {
348 if (handler == null) {
349 byte[] data = IOUtils.readBytesAndClose(in, (int) length);
350 return createSmallLob(Value.BLOB, data);
351 }
352 long remaining = Long.MAX_VALUE;
353 boolean compress = handler.getLobCompressionAlgorithm(Value.BLOB) != null;
354 if (length >= 0 && length < remaining) {
355 remaining = length;
356 }
357 int len = getBufferSize(handler, compress, remaining);
358 byte[] buff;
359 if (len >= Integer.MAX_VALUE) {
360 buff = IOUtils.readBytesAndClose(in, -1);
361 len = buff.length;
362 } else {
363 buff = Utils.newBytes(len);
364 len = IOUtils.readFully(in, buff, 0, len);
365 }
366 if (len <= handler.getMaxLengthInplaceLob()) {
367 byte[] small = Utils.newBytes(len);
368 System.arraycopy(buff, 0, small, 0, len);
369 return ValueLob.createSmallLob(Value.BLOB, small);
370 }
371 ValueLob lob = new ValueLob(Value.BLOB, null);
372 lob.createFromStream(buff, len, in, remaining, handler);
373 return lob;
374 } catch (IOException e) {
375 throw DbException.convertIOException(e, null);
376 }
377 }
378
379 private FileStoreOutputStream initLarge(DataHandler h) {
380 this.handler = h;
381 this.tableId = 0;
382 this.linked = false;
383 this.precision = 0;
384 this.small = null;
385 this.hash = 0;
386 String compressionAlgorithm = h.getLobCompressionAlgorithm(type);
387 this.compression = compressionAlgorithm != null;
388 synchronized (h) {
389 String path = h.getDatabasePath();
390 if ((path != null) && (path.length() == 0)) {
391 path = new File(Utils.getProperty("java.io.tmpdir", "."), SysProperties.PREFIX_TEMP_FILE).getAbsolutePath();
392 }
393 objectId = getNewObjectId(h);
394 fileName = getFileNamePrefix(path, objectId) + Constants.SUFFIX_TEMP_FILE;
395 tempFile = h.openFile(fileName, "rw", false);
396 tempFile.autoDelete();
397 }
398 FileStoreOutputStream out = new FileStoreOutputStream(tempFile, h, compressionAlgorithm);
399 return out;
400 }
401
402 private void createFromStream(byte[] buff, int len, InputStream in, long remaining, DataHandler h) {
403 try {
404 FileStoreOutputStream out = initLarge(h);
405 boolean compress = h.getLobCompressionAlgorithm(Value.BLOB) != null;
406 try {
407 while (true) {
408 precision += len;
409 out.write(buff, 0, len);
410 remaining -= len;
411 if (remaining <= 0) {
412 break;
413 }
414 len = getBufferSize(h, compress, remaining);
415 len = IOUtils.readFully(in, buff, 0, len);
416 if (len <= 0) {
417 break;
418 }
419 }
420 } finally {
421 out.close();
422 }
423 } catch (IOException e) {
424 throw DbException.convertIOException(e, null);
425 }
426 }
427
428 /**
429 * Convert a lob to another data type. The data is fully read in memory
430 * except when converting to BLOB or CLOB.
431 *
432 * @param t the new type
433 * @return the converted value
434 */
435 public Value convertTo(int t) {
436 if (t == type) {
437 return this;
438 } else if (t == Value.CLOB) {
439 ValueLob copy = ValueLob.createClob(getReader(), -1, handler);
440 return copy;
441 } else if (t == Value.BLOB) {
442 ValueLob copy = ValueLob.createBlob(getInputStream(), -1, handler);
443 return copy;
444 }
445 return super.convertTo(t);
446 }
447
448 public boolean isLinked() {
449 return linked;
450 }
451
452 /**
453 * Get the current file name where the lob is saved.
454 *
455 * @return the file name or null
456 */
457 public String getFileName() {
458 return fileName;
459 }
460
461 public void close() {
462 if (fileName != null) {
463 if (tempFile != null) {
464 tempFile.stopAutoDelete();
465 }
466 deleteFile(handler, fileName);
467 }
468 }
469
470 public void unlink() {
471 if (linked && fileName != null) {
472 String temp;
473 // synchronize on the database, to avoid concurrent temp file
474 // creation / deletion / backup
475 synchronized (handler) {
476 temp = getFileName(handler, -1, objectId);
477 deleteFile(handler, temp);
478 renameFile(handler, fileName, temp);
479 tempFile = FileStore.open(handler, temp, "rw");
480 tempFile.autoDelete();
481 tempFile.closeSilently();
482 fileName = temp;
483 linked = false;
484 }
485 }
486 }
487
488 public Value link(DataHandler h, int tabId) {
489 if (fileName == null) {
490 this.tableId = tabId;
491 return this;
492 }
493 if (linked) {
494 ValueLob copy = ValueLob.copy(this);
495 copy.objectId = getNewObjectId(h);
496 copy.tableId = tabId;
497 String live = getFileName(h, copy.tableId, copy.objectId);
498 copyFileTo(h, fileName, live);
499 copy.fileName = live;
500 copy.linked = true;
501 return copy;
502 }
503 if (!linked) {
504 this.tableId = tabId;
505 String live = getFileName(h, tableId, objectId);
506 if (tempFile != null) {
507 tempFile.stopAutoDelete();
508 tempFile = null;
509 }
510 renameFile(h, fileName, live);
511 fileName = live;
512 linked = true;
513 }
514 return this;
515 }
516
517 /**
518 * Get the current table id of this lob.
519 *
520 * @return the table id
521 */
522 public int getTableId() {
523 return tableId;
524 }
525
526 /**
527 * Get the current object id of this lob.
528 *
529 * @return the object id
530 */
531 public int getObjectId() {
532 return objectId;
533 }
534
535 public int getType() {
536 return type;
537 }
538
539 public long getPrecision() {
540 return precision;
541 }
542
543 public String getString() {
544 int len = precision > Integer.MAX_VALUE || precision == 0 ? Integer.MAX_VALUE : (int) precision;
545 try {
546 if (type == Value.CLOB) {
547 if (small != null) {
548 return StringUtils.utf8Decode(small);
549 }
550 return IOUtils.readStringAndClose(getReader(), len);
551 }
552 byte[] buff;
553 if (small != null) {
554 buff = small;
555 } else {
556 buff = IOUtils.readBytesAndClose(getInputStream(), len);
557 }
558 return StringUtils.convertBytesToHex(buff);
559 } catch (IOException e) {
560 throw DbException.convertIOException(e, fileName);
561 }
562 }
563
564 public byte[] getBytes() {
565 if (type == CLOB) {
566 // convert hex to string
567 return super.getBytes();
568 }
569 byte[] data = getBytesNoCopy();
570 return Utils.cloneByteArray(data);
571 }
572
573 public byte[] getBytesNoCopy() {
574 if (type == CLOB) {
575 // convert hex to string
576 return super.getBytesNoCopy();
577 }
578 if (small != null) {
579 return small;
580 }
581 try {
582 return IOUtils.readBytesAndClose(getInputStream(), Integer.MAX_VALUE);
583 } catch (IOException e) {
584 throw DbException.convertIOException(e, fileName);
585 }
586 }
587
588 public int hashCode() {
589 if (hash == 0) {
590 if (precision > 4096) {
591 // TODO: should calculate the hash code when saving, and store
592 // it in the database file
593 return (int) (precision ^ (precision >>> 32));
594 }
595 if (type == CLOB) {
596 hash = getString().hashCode();
597 } else {
598 hash = Utils.getByteArrayHash(getBytes());
599 }
600 }
601 return hash;
602 }
603
604 protected int compareSecure(Value v, CompareMode mode) {
605 if (type == Value.CLOB) {
606 return Integer.signum(getString().compareTo(v.getString()));
607 }
608 byte[] v2 = v.getBytesNoCopy();
609 return Utils.compareNotNull(getBytes(), v2);
610 }
611
612 public Object getObject() {
613 if (type == Value.CLOB) {
614 return getReader();
615 }
616 return getInputStream();
617 }
618
619 public Reader getReader() {
620 return IOUtils.getBufferedReader(getInputStream());
621 }
622
623 public InputStream getInputStream() {
624 if (fileName == null) {
625 return new ByteArrayInputStream(small);
626 }
627 FileStore store = handler.openFile(fileName, "r", true);
628 boolean alwaysClose = SysProperties.lobCloseBetweenReads;
629 return new BufferedInputStream(new FileStoreInputStream(store, handler, compression, alwaysClose),
630 Constants.IO_BUFFER_SIZE);
631 }
632
633 public void set(PreparedStatement prep, int parameterIndex) throws SQLException {
634 long p = getPrecision();
635 if (p > Integer.MAX_VALUE || p <= 0) {
636 p = -1;
637 }
638 if (type == Value.BLOB) {
639 prep.setBinaryStream(parameterIndex, getInputStream(), (int) p);
640 } else {
641 prep.setCharacterStream(parameterIndex, getReader(), (int) p);
642 }
643 }
644
645 public String getSQL() {
646 String s;
647 if (type == Value.CLOB) {
648 s = getString();
649 return StringUtils.quoteStringSQL(s);
650 }
651 byte[] buff = getBytes();
652 s = StringUtils.convertBytesToHex(buff);
653 return "X'" + s + "'";
654 }
655
656 public String getTraceSQL() {
657 if (small != null && getPrecision() <= SysProperties.MAX_TRACE_DATA_LENGTH) {
658 return getSQL();
659 }
660 StringBuilder buff = new StringBuilder();
661 if (type == Value.CLOB) {
662 buff.append("SPACE(").append(getPrecision());
663 } else {
664 buff.append("CAST(REPEAT('00', ").append(getPrecision()).append(") AS BINARY");
665 }
666 buff.append(" /* ").append(fileName).append(" */)");
667 return buff.toString();
668 }
669
670 /**
671 * Get the data if this a small lob value.
672 *
673 * @return the data
674 */
675 public byte[] getSmall() {
676 return small;
677 }
678
679 public int getDisplaySize() {
680 return MathUtils.convertLongToInt(getPrecision());
681 }
682
683 public boolean equals(Object other) {
684 return other instanceof ValueLob && compareSecure((Value) other, null) == 0;
685 }
686
687 /**
688 * Store the lob data to a file if the size of the buffer it larger than the
689 * maximum size for an in-place lob.
690 *
691 * @param h the data handler
692 */
693 public void convertToFileIfRequired(DataHandler h) {
694 if (small != null && small.length > h.getMaxLengthInplaceLob()) {
695 boolean compress = h.getLobCompressionAlgorithm(type) != null;
696 int len = getBufferSize(h, compress, Long.MAX_VALUE);
697 int tabId = tableId;
698 if (type == Value.BLOB) {
699 createFromStream(Utils.newBytes(len), 0, getInputStream(), Long.MAX_VALUE, h);
700 } else {
701 createFromReader(new char[len], 0, getReader(), Long.MAX_VALUE, h);
702 }
703 Value v2 = link(h, tabId);
704 if (SysProperties.CHECK && v2 != this) {
705 DbException.throwInternalError();
706 }
707 }
708 }
709
710 /**
711 * Remove all lobs for a given table id.
712 *
713 * @param handler the data handler
714 * @param tableId the table id
715 */
716 public static void removeAllForTable(DataHandler handler, int tableId) {
717 String dir = getFileNamePrefix(handler.getDatabasePath(), 0);
718 removeAllForTable(handler, dir, tableId);
719 }
720
721 private static void removeAllForTable(DataHandler handler, String dir, int tableId) {
722 for (String name : FileUtils.newDirectoryStream(dir)) {
723 if (FileUtils.isDirectory(name)) {
724 removeAllForTable(handler, name, tableId);
725 } else {
726 if (name.endsWith(".t" + tableId + Constants.SUFFIX_LOB_FILE)) {
727 deleteFile(handler, name);
728 }
729 }
730 }
731 }
732
733 /**
734 * Check if this lob value is compressed.
735 *
736 * @return true if it is
737 */
738 public boolean useCompression() {
739 return compression;
740 }
741
742 private static synchronized void deleteFile(DataHandler handler, String fileName) {
743 // synchronize on the database, to avoid concurrent temp file creation /
744 // deletion / backup
745 synchronized (handler.getLobSyncObject()) {
746 FileUtils.delete(fileName);
747 }
748 }
749
750 private static synchronized void renameFile(DataHandler handler, String oldName, String newName)
751 {
752 synchronized (handler.getLobSyncObject()) {
753 FileUtils.moveTo(oldName, newName);
754 }
755 }
756
757 private static void copyFileTo(DataHandler h, String sourceFileName, String targetFileName) {
758 synchronized (h.getLobSyncObject()) {
759 try {
760 FileUtils.copy(sourceFileName, targetFileName);
761 } catch (IOException e) {
762 throw DbException.convertIOException(e, null);
763 }
764 }
765 }
766
767 /**
768 * Set the file name of this lob value.
769 *
770 * @param fileName the file name
771 * @param linked if the lob is linked
772 */
773 public void setFileName(String fileName, boolean linked) {
774 this.fileName = fileName;
775 this.linked = linked;
776 }
777
778 public int getMemory() {
779 if (small != null) {
780 return small.length + 104;
781 }
782 return 140;
783 }
784
785 /**
786 * Create an independent copy of this temporary value.
787 * The file will not be deleted automatically.
788 *
789 * @return the value
790 */
791 public ValueLob copyToTemp() {
792 ValueLob lob;
793 if (type == CLOB) {
794 lob = ValueLob.createClob(getReader(), precision, handler);
795 } else {
796 lob = ValueLob.createBlob(getInputStream(), precision, handler);
797 }
798 return lob;
799 }
800
801 public Value convertPrecision(long precision, boolean force) {
802 if (this.precision <= precision) {
803 return this;
804 }
805 ValueLob lob;
806 if (type == CLOB) {
807 lob = ValueLob.createClob(getReader(), precision, handler);
808 } else {
809 lob = ValueLob.createBlob(getInputStream(), precision, handler);
810 }
811 return lob;
812 }
813
814 }
815