EMMA Coverage Report (generated Sun Mar 01 22:06:14 CET 2015)
[all classes][org.h2.value]

COVERAGE SUMMARY FOR SOURCE FILE [ValueLobDb.java]

nameclass, %method, %block, %line, %
ValueLobDb.java100% (1/1)92%  (37/40)81%  (992/1229)80%  (228.6/286)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ValueLobDb100% (1/1)92%  (37/40)81%  (992/1229)80%  (228.6/286)
ValueLobDb (DataHandler, byte [], int, InputStream, long): void 0%   (0/1)0%   (0/98)0%   (0/25)
copyToTemp (): ValueLobDb 0%   (0/1)0%   (0/2)0%   (0/1)
equals (Object): boolean 0%   (0/1)0%   (0/13)0%   (0/1)
hashCode (): int 100% (1/1)60%  (21/35)71%  (5/7)
convertPrecision (long, boolean): Value 100% (1/1)70%  (57/82)71%  (15/21)
getBytesNoCopy (): byte [] 100% (1/1)75%  (18/24)71%  (5/7)
createTempBlob (InputStream, long, DataHandler): ValueLobDb 100% (1/1)76%  (61/80)78%  (14/18)
getBytes (): byte [] 100% (1/1)77%  (10/13)75%  (3/4)
compareSecure (Value, CompareMode): int 100% (1/1)77%  (34/44)75%  (7.5/10)
convertTo (int): Value 100% (1/1)83%  (48/58)87%  (13/15)
close (): void 100% (1/1)86%  (30/35)97%  (8.7/9)
createTempClob (Reader, long, DataHandler): ValueLobDb 100% (1/1)89%  (87/98)83%  (20/24)
getString (): String 100% (1/1)89%  (50/56)82%  (9/11)
getInputStream (): InputStream 100% (1/1)90%  (53/59)80%  (8/10)
ValueLobDb (DataHandler, Reader, long): void 100% (1/1)93%  (68/73)97%  (20.4/21)
set (PreparedStatement, int): void 100% (1/1)94%  (31/33)86%  (6/7)
getBufferSize (DataHandler, boolean, long): int 100% (1/1)96%  (54/56)92%  (11/12)
ValueLobDb (int, DataHandler, int, long, byte [], long): void 100% (1/1)100% (30/30)100% (11/11)
ValueLobDb (int, byte [], long): void 100% (1/1)100% (27/27)100% (10/10)
copyToResult (): ValueLobDb 100% (1/1)100% (21/21)100% (6/6)
create (int, DataHandler, int, long, byte [], long): ValueLobDb 100% (1/1)100% (10/10)100% (1/1)
createSmallLob (int, byte []): Value 100% (1/1)100% (20/20)100% (4/4)
createSmallLob (int, byte [], long): ValueLobDb 100% (1/1)100% (7/7)100% (1/1)
createTempLobFileName (DataHandler): String 100% (1/1)100% (14/14)100% (4/4)
getDisplaySize (): int 100% (1/1)100% (4/4)100% (1/1)
getLobId (): long 100% (1/1)100% (3/3)100% (1/1)
getMemory (): int 100% (1/1)100% (11/11)100% (3/3)
getObject (): Object 100% (1/1)100% (10/10)100% (3/3)
getPrecision (): long 100% (1/1)100% (3/3)100% (1/1)
getReader (): Reader 100% (1/1)100% (4/4)100% (1/1)
getSQL (): String 100% (1/1)100% (27/27)100% (6/6)
getSmall (): byte [] 100% (1/1)100% (3/3)100% (1/1)
getTableId (): int 100% (1/1)100% (3/3)100% (1/1)
getTraceSQL (): String 100% (1/1)100% (53/53)100% (8/8)
getType (): int 100% (1/1)100% (3/3)100% (1/1)
isLinked (): boolean 100% (1/1)100% (11/11)100% (1/1)
isStored (): boolean 100% (1/1)100% (10/10)100% (1/1)
link (DataHandler, int): Value 100% (1/1)100% (60/60)100% (12/12)
toString (): String 100% (1/1)100% (20/20)100% (1/1)
unlink (DataHandler): void 100% (1/1)100% (16/16)100% (4/4)

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

[all classes][org.h2.value]
EMMA 2.0.5312 (C) Vladimir Roubtsov