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

COVERAGE SUMMARY FOR SOURCE FILE [MVPrimaryIndex.java]

nameclass, %method, %block, %line, %
MVPrimaryIndex.java100% (2/2)82%  (28/34)92%  (673/735)88%  (140/160)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class MVPrimaryIndex100% (1/1)83%  (24/29)91%  (590/649)87%  (124/143)
canGetFirstOrLast (): boolean 0%   (0/1)0%   (0/2)0%   (0/1)
checkRename (): void 0%   (0/1)0%   (0/1)0%   (0/1)
close (Session): void 0%   (0/1)0%   (0/1)0%   (0/1)
getDiskSpaceUsed (): long 0%   (0/1)0%   (0/2)0%   (0/1)
needRebuild (): boolean 0%   (0/1)0%   (0/2)0%   (0/1)
getRowCountMax (): long 100% (1/1)36%  (4/11)33%  (1/3)
find (Session, SearchRow, SearchRow): Cursor 100% (1/1)57%  (39/69)56%  (10/18)
getCost (Session, int [], TableFilter, SortOrder): double 100% (1/1)61%  (11/18)50%  (2/4)
getMap (Session): TransactionStore$TransactionMap 100% (1/1)81%  (13/16)75%  (3/4)
getKey (SearchRow, ValueLong, ValueLong): ValueLong 100% (1/1)84%  (21/25)88%  (7/8)
<static initializer> 100% (1/1)100% (10/10)100% (3/3)
MVPrimaryIndex (Database, MVTable, int, IndexColumn [], IndexType): void 100% (1/1)100% (98/98)100% (16/16)
add (Session, Row): void 100% (1/1)100% (157/157)100% (29/29)
find (Session, ValueLong, ValueLong): Cursor 100% (1/1)100% (13/13)100% (2/2)
findFirstOrLast (Session, boolean): Cursor 100% (1/1)100% (58/58)100% (10/10)
getColumnIndex (Column): int 100% (1/1)100% (2/2)100% (1/1)
getCreateSQL (): String 100% (1/1)100% (2/2)100% (1/1)
getMainIndexColumn (): int 100% (1/1)100% (3/3)100% (1/1)
getMapName (): String 100% (1/1)100% (3/3)100% (1/1)
getPlanSQL (): String 100% (1/1)100% (11/11)100% (1/1)
getRow (Session, long): Row 100% (1/1)100% (25/25)100% (6/6)
getRowCount (Session): long 100% (1/1)100% (7/7)100% (2/2)
getRowCountApproximation (): long 100% (1/1)100% (3/3)100% (1/1)
getTable (): MVTable 100% (1/1)100% (3/3)100% (1/1)
isRowIdIndex (): boolean 100% (1/1)100% (2/2)100% (1/1)
remove (Session): void 100% (1/1)100% (16/16)100% (5/5)
remove (Session, Row): void 100% (1/1)100% (67/67)100% (13/13)
setMainIndexColumn (int): void 100% (1/1)100% (4/4)100% (2/2)
truncate (Session): void 100% (1/1)100% (18/18)100% (5/5)
     
class MVPrimaryIndex$MVStoreCursor100% (1/1)80%  (4/5)97%  (83/86)94%  (16/17)
previous (): boolean 0%   (0/1)0%   (0/3)0%   (0/1)
MVPrimaryIndex$MVStoreCursor (MVPrimaryIndex, Iterator, ValueLong): void 100% (1/1)100% (12/12)100% (4/4)
get (): Row 100% (1/1)100% (30/30)100% (6/6)
getSearchRow (): SearchRow 100% (1/1)100% (3/3)100% (1/1)
next (): boolean 100% (1/1)100% (38/38)100% (5/5)

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.mvstore.db;
7 
8import java.util.Arrays;
9import java.util.Collections;
10import java.util.Iterator;
11import java.util.List;
12import java.util.Map.Entry;
13 
14import org.h2.api.ErrorCode;
15import org.h2.engine.Constants;
16import org.h2.engine.Database;
17import org.h2.engine.Session;
18import org.h2.index.BaseIndex;
19import org.h2.index.Cursor;
20import org.h2.index.IndexType;
21import org.h2.message.DbException;
22import org.h2.mvstore.DataUtils;
23import org.h2.mvstore.db.TransactionStore.Transaction;
24import org.h2.mvstore.db.TransactionStore.TransactionMap;
25import org.h2.result.Row;
26import org.h2.result.SearchRow;
27import org.h2.result.SortOrder;
28import org.h2.table.Column;
29import org.h2.table.IndexColumn;
30import org.h2.table.TableFilter;
31import org.h2.value.Value;
32import org.h2.value.ValueArray;
33import org.h2.value.ValueLong;
34import org.h2.value.ValueNull;
35 
36/**
37 * A table stored in a MVStore.
38 */
39public class MVPrimaryIndex extends BaseIndex {
40 
41    /**
42     * The minimum long value.
43     */
44    static final ValueLong MIN = ValueLong.get(Long.MIN_VALUE);
45 
46    /**
47     * The maximum long value.
48     */
49    static final ValueLong MAX = ValueLong.get(Long.MAX_VALUE);
50 
51    /**
52     * The zero long value.
53     */
54    static final ValueLong ZERO = ValueLong.get(0);
55 
56    private final MVTable mvTable;
57    private final String mapName;
58    private TransactionMap<Value, Value> dataMap;
59    private long lastKey;
60    private int mainIndexColumn = -1;
61 
62    public MVPrimaryIndex(Database db, MVTable table, int id,
63            IndexColumn[] columns, IndexType indexType) {
64        this.mvTable = table;
65        initBaseIndex(table, id, table.getName() + "_DATA", columns, indexType);
66        int[] sortTypes = new int[columns.length];
67        for (int i = 0; i < columns.length; i++) {
68            sortTypes[i] = SortOrder.ASCENDING;
69        }
70        ValueDataType keyType = new ValueDataType(null, null, null);
71        ValueDataType valueType = new ValueDataType(db.getCompareMode(), db,
72                sortTypes);
73        mapName = "table." + getId();
74        dataMap = mvTable.getTransaction(null).openMap(mapName, keyType,
75                valueType);
76        if (!table.isPersistData()) {
77            dataMap.map.setVolatile(true);
78        }
79        Value k = dataMap.lastKey();
80        lastKey = k == null ? 0 : k.getLong();
81    }
82 
83    @Override
84    public String getCreateSQL() {
85        return null;
86    }
87 
88    @Override
89    public String getPlanSQL() {
90        return table.getSQL() + ".tableScan";
91    }
92 
93    public void setMainIndexColumn(int mainIndexColumn) {
94        this.mainIndexColumn = mainIndexColumn;
95    }
96 
97    public int getMainIndexColumn() {
98        return mainIndexColumn;
99    }
100 
101    @Override
102    public void close(Session session) {
103        // ok
104    }
105 
106    @Override
107    public void add(Session session, Row row) {
108        if (mainIndexColumn == -1) {
109            if (row.getKey() == 0) {
110                row.setKey(++lastKey);
111            }
112        } else {
113            long c = row.getValue(mainIndexColumn).getLong();
114            row.setKey(c);
115        }
116 
117        if (mvTable.getContainsLargeObject()) {
118            for (int i = 0, len = row.getColumnCount(); i < len; i++) {
119                Value v = row.getValue(i);
120                Value v2 = v.link(database, getId());
121                if (v2.isLinked()) {
122                    session.unlinkAtCommitStop(v2);
123                }
124                if (v != v2) {
125                    row.setValue(i, v2);
126                }
127            }
128        }
129 
130        TransactionMap<Value, Value> map = getMap(session);
131        Value key = ValueLong.get(row.getKey());
132        Value old = map.getLatest(key);
133        if (old != null) {
134            String sql = "PRIMARY KEY ON " + table.getSQL();
135            if (mainIndexColumn >= 0 && mainIndexColumn < indexColumns.length) {
136                sql += "(" + indexColumns[mainIndexColumn].getSQL() + ")";
137            }
138            DbException e = DbException.get(ErrorCode.DUPLICATE_KEY_1, sql);
139            e.setSource(this);
140            throw e;
141        }
142        try {
143            map.put(key, ValueArray.get(row.getValueList()));
144        } catch (IllegalStateException e) {
145            throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1,
146                    e, table.getName());
147        }
148        lastKey = Math.max(lastKey, row.getKey());
149    }
150 
151    @Override
152    public void remove(Session session, Row row) {
153        if (mvTable.getContainsLargeObject()) {
154            for (int i = 0, len = row.getColumnCount(); i < len; i++) {
155                Value v = row.getValue(i);
156                if (v.isLinked()) {
157                    session.unlinkAtCommit(v);
158                }
159            }
160        }
161        TransactionMap<Value, Value> map = getMap(session);
162        try {
163            Value old = map.remove(ValueLong.get(row.getKey()));
164            if (old == null) {
165                throw DbException.get(ErrorCode.ROW_NOT_FOUND_WHEN_DELETING_1,
166                        getSQL() + ": " + row.getKey());
167            }
168        } catch (IllegalStateException e) {
169            throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1,
170                    e, table.getName());
171        }
172    }
173 
174    @Override
175    public Cursor find(Session session, SearchRow first, SearchRow last) {
176        ValueLong min, max;
177        if (first == null) {
178            min = MIN;
179        } else if (mainIndexColumn < 0) {
180            min = ValueLong.get(first.getKey());
181        } else {
182            ValueLong v = (ValueLong) first.getValue(mainIndexColumn);
183            if (v == null) {
184                min = ValueLong.get(first.getKey());
185            } else {
186                min = v;
187            }
188        }
189        if (last == null) {
190            max = MAX;
191        } else if (mainIndexColumn < 0) {
192            max = ValueLong.get(last.getKey());
193        } else {
194            ValueLong v = (ValueLong) last.getValue(mainIndexColumn);
195            if (v == null) {
196                max = ValueLong.get(last.getKey());
197            } else {
198                max = v;
199            }
200        }
201        TransactionMap<Value, Value> map = getMap(session);
202        return new MVStoreCursor(map.entryIterator(min), max);
203    }
204 
205    @Override
206    public MVTable getTable() {
207        return mvTable;
208    }
209 
210    @Override
211    public Row getRow(Session session, long key) {
212        TransactionMap<Value, Value> map = getMap(session);
213        Value v = map.get(ValueLong.get(key));
214        ValueArray array = (ValueArray) v;
215        Row row = new Row(array.getList(), 0);
216        row.setKey(key);
217        return row;
218    }
219 
220    @Override
221    public double getCost(Session session, int[] masks, TableFilter filter,
222            SortOrder sortOrder) {
223        try {
224            long cost = 10 * (dataMap.sizeAsLongMax() + Constants.COST_ROW_OFFSET);
225            return cost;
226        } catch (IllegalStateException e) {
227            throw DbException.get(ErrorCode.OBJECT_CLOSED, e);
228        }
229    }
230 
231    @Override
232    public int getColumnIndex(Column col) {
233        // can not use this index - use the delegate index instead
234        return -1;
235    }
236 
237    @Override
238    public void remove(Session session) {
239        TransactionMap<Value, Value> map = getMap(session);
240        if (!map.isClosed()) {
241            Transaction t = mvTable.getTransaction(session);
242            t.removeMap(map);
243        }
244    }
245 
246    @Override
247    public void truncate(Session session) {
248        TransactionMap<Value, Value> map = getMap(session);
249        if (mvTable.getContainsLargeObject()) {
250            database.getLobStorage().removeAllForTable(table.getId());
251        }
252        map.clear();
253    }
254 
255    @Override
256    public boolean canGetFirstOrLast() {
257        return true;
258    }
259 
260    @Override
261    public Cursor findFirstOrLast(Session session, boolean first) {
262        TransactionMap<Value, Value> map = getMap(session);
263        ValueLong v = (ValueLong) (first ? map.firstKey() : map.lastKey());
264        if (v == null) {
265            return new MVStoreCursor(Collections
266                    .<Entry<Value, Value>> emptyList().iterator(), null);
267        }
268        Value value = map.get(v);
269        Entry<Value, Value> e = new DataUtils.MapEntry<Value, Value>(v, value);
270        @SuppressWarnings("unchecked")
271        List<Entry<Value, Value>> list = Arrays.asList(e);
272        MVStoreCursor c = new MVStoreCursor(list.iterator(), v);
273        c.next();
274        return c;
275    }
276 
277    @Override
278    public boolean needRebuild() {
279        return false;
280    }
281 
282    @Override
283    public long getRowCount(Session session) {
284        TransactionMap<Value, Value> map = getMap(session);
285        return map.sizeAsLong();
286    }
287 
288    /**
289     * The maximum number of rows, including uncommitted rows of any session.
290     *
291     * @return the maximum number of rows
292     */
293    public long getRowCountMax() {
294        try {
295            return dataMap.sizeAsLongMax();
296        } catch (IllegalStateException e) {
297            throw DbException.get(ErrorCode.OBJECT_CLOSED, e);
298        }
299    }
300 
301    @Override
302    public long getRowCountApproximation() {
303        return getRowCountMax();
304    }
305 
306    @Override
307    public long getDiskSpaceUsed() {
308        // TODO estimate disk space usage
309        return 0;
310    }
311 
312    public String getMapName() {
313        return mapName;
314    }
315 
316    @Override
317    public void checkRename() {
318        // ok
319    }
320 
321    /**
322     * Get the key from the row.
323     *
324     * @param row the row
325     * @param ifEmpty the value to use if the row is empty
326     * @param ifNull the value to use if the column is NULL
327     * @return the key
328     */
329    ValueLong getKey(SearchRow row, ValueLong ifEmpty, ValueLong ifNull) {
330        if (row == null) {
331            return ifEmpty;
332        }
333        Value v = row.getValue(mainIndexColumn);
334        if (v == null) {
335            throw DbException.throwInternalError(row.toString());
336        } else if (v == ValueNull.INSTANCE) {
337            return ifNull;
338        }
339        return (ValueLong) v.convertTo(Value.LONG);
340    }
341 
342    /**
343     * Search for a specific row or a set of rows.
344     *
345     * @param session the session
346     * @param first the key of the first row
347     * @param last the key of the last row
348     * @return the cursor
349     */
350    Cursor find(Session session, ValueLong first, ValueLong last) {
351        TransactionMap<Value, Value> map = getMap(session);
352        return new MVStoreCursor(map.entryIterator(first), last);
353    }
354 
355    @Override
356    public boolean isRowIdIndex() {
357        return true;
358    }
359 
360    /**
361     * Get the map to store the data.
362     *
363     * @param session the session
364     * @return the map
365     */
366    TransactionMap<Value, Value> getMap(Session session) {
367        if (session == null) {
368            return dataMap;
369        }
370        Transaction t = mvTable.getTransaction(session);
371        return dataMap.getInstance(t, Long.MAX_VALUE);
372    }
373 
374    /**
375     * A cursor.
376     */
377    class MVStoreCursor implements Cursor {
378 
379        private final Iterator<Entry<Value, Value>> it;
380        private final ValueLong last;
381        private Entry<Value, Value> current;
382        private Row row;
383 
384        public MVStoreCursor(Iterator<Entry<Value, Value>> it, ValueLong last) {
385            this.it = it;
386            this.last = last;
387        }
388 
389        @Override
390        public Row get() {
391            if (row == null) {
392                if (current != null) {
393                    ValueArray array = (ValueArray) current.getValue();
394                    row = new Row(array.getList(), 0);
395                    row.setKey(current.getKey().getLong());
396                }
397            }
398            return row;
399        }
400 
401        @Override
402        public SearchRow getSearchRow() {
403            return get();
404        }
405 
406        @Override
407        public boolean next() {
408            current = it.hasNext() ? it.next() : null;
409            if (current != null && current.getKey().getLong() > last.getLong()) {
410                current = null;
411            }
412            row = null;
413            return current != null;
414        }
415 
416        @Override
417        public boolean previous() {
418            throw DbException.getUnsupportedException("previous");
419        }
420 
421    }
422 
423}

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