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.index; |
7 | |
8 | import org.h2.engine.Session; |
9 | import org.h2.message.DbException; |
10 | import org.h2.result.Row; |
11 | import org.h2.result.SearchRow; |
12 | import org.h2.result.SortOrder; |
13 | import org.h2.table.Column; |
14 | import org.h2.table.IndexColumn; |
15 | import org.h2.table.RegularTable; |
16 | import org.h2.table.TableFilter; |
17 | import org.h2.util.ValueHashMap; |
18 | import org.h2.value.Value; |
19 | |
20 | /** |
21 | * An unique index based on an in-memory hash map. |
22 | */ |
23 | public class HashIndex extends BaseIndex { |
24 | |
25 | /** |
26 | * The index of the indexed column. |
27 | */ |
28 | private final int indexColumn; |
29 | |
30 | private final RegularTable tableData; |
31 | private ValueHashMap<Long> rows; |
32 | |
33 | public HashIndex(RegularTable table, int id, String indexName, |
34 | IndexColumn[] columns, IndexType indexType) { |
35 | initBaseIndex(table, id, indexName, columns, indexType); |
36 | this.indexColumn = columns[0].column.getColumnId(); |
37 | this.tableData = table; |
38 | reset(); |
39 | } |
40 | |
41 | private void reset() { |
42 | rows = ValueHashMap.newInstance(); |
43 | } |
44 | |
45 | @Override |
46 | public void truncate(Session session) { |
47 | reset(); |
48 | } |
49 | |
50 | @Override |
51 | public void add(Session session, Row row) { |
52 | Value key = row.getValue(indexColumn); |
53 | Object old = rows.get(key); |
54 | if (old != null) { |
55 | // TODO index duplicate key for hash indexes: is this allowed? |
56 | throw getDuplicateKeyException(key.toString()); |
57 | } |
58 | rows.put(key, row.getKey()); |
59 | } |
60 | |
61 | @Override |
62 | public void remove(Session session, Row row) { |
63 | rows.remove(row.getValue(indexColumn)); |
64 | } |
65 | |
66 | @Override |
67 | public Cursor find(Session session, SearchRow first, SearchRow last) { |
68 | if (first == null || last == null) { |
69 | // TODO hash index: should additionally check if values are the same |
70 | throw DbException.throwInternalError(); |
71 | } |
72 | Value v = first.getValue(indexColumn); |
73 | /* |
74 | * Sometimes the incoming search is a similar, but not the same type |
75 | * e.g. the search value is INT, but the index column is LONG. In which |
76 | * case we need to convert, otherwise the ValueHashMap will not find the |
77 | * result. |
78 | */ |
79 | v = v.convertTo(tableData.getColumn(indexColumn).getType()); |
80 | Row result; |
81 | Long pos = rows.get(v); |
82 | if (pos == null) { |
83 | result = null; |
84 | } else { |
85 | result = tableData.getRow(session, pos.intValue()); |
86 | } |
87 | return new SingleRowCursor(result); |
88 | } |
89 | |
90 | @Override |
91 | public long getRowCount(Session session) { |
92 | return getRowCountApproximation(); |
93 | } |
94 | |
95 | @Override |
96 | public long getRowCountApproximation() { |
97 | return rows.size(); |
98 | } |
99 | |
100 | @Override |
101 | public long getDiskSpaceUsed() { |
102 | return 0; |
103 | } |
104 | |
105 | @Override |
106 | public void close(Session session) { |
107 | // nothing to do |
108 | } |
109 | |
110 | @Override |
111 | public void remove(Session session) { |
112 | // nothing to do |
113 | } |
114 | |
115 | @Override |
116 | public double getCost(Session session, int[] masks, TableFilter filter, |
117 | SortOrder sortOrder) { |
118 | for (Column column : columns) { |
119 | int index = column.getColumnId(); |
120 | int mask = masks[index]; |
121 | if ((mask & IndexCondition.EQUALITY) != IndexCondition.EQUALITY) { |
122 | return Long.MAX_VALUE; |
123 | } |
124 | } |
125 | return 2; |
126 | } |
127 | |
128 | @Override |
129 | public void checkRename() { |
130 | // ok |
131 | } |
132 | |
133 | @Override |
134 | public boolean needRebuild() { |
135 | return true; |
136 | } |
137 | |
138 | @Override |
139 | public boolean canGetFirstOrLast() { |
140 | return false; |
141 | } |
142 | |
143 | @Override |
144 | public Cursor findFirstOrLast(Session session, boolean first) { |
145 | throw DbException.getUnsupportedException("HASH"); |
146 | } |
147 | |
148 | @Override |
149 | public boolean canScan() { |
150 | return false; |
151 | } |
152 | |
153 | } |