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.result.Row; |
10 | import org.h2.store.Data; |
11 | import org.h2.store.Page; |
12 | |
13 | /** |
14 | * A page that contains data rows. |
15 | */ |
16 | abstract class PageData extends Page { |
17 | |
18 | /** |
19 | * The position of the parent page id. |
20 | */ |
21 | static final int START_PARENT = 3; |
22 | |
23 | /** |
24 | * This is a root page. |
25 | */ |
26 | static final int ROOT = 0; |
27 | |
28 | /** |
29 | * Indicator that the row count is not known. |
30 | */ |
31 | static final int UNKNOWN_ROWCOUNT = -1; |
32 | |
33 | /** |
34 | * The index. |
35 | */ |
36 | protected final PageDataIndex index; |
37 | |
38 | /** |
39 | * The page number of the parent. |
40 | */ |
41 | protected int parentPageId; |
42 | |
43 | /** |
44 | * The data page. |
45 | */ |
46 | protected final Data data; |
47 | |
48 | /** |
49 | * The number of entries. |
50 | */ |
51 | protected int entryCount; |
52 | |
53 | /** |
54 | * The row keys. |
55 | */ |
56 | protected long[] keys; |
57 | |
58 | /** |
59 | * Whether the data page is up-to-date. |
60 | */ |
61 | protected boolean written; |
62 | |
63 | /** |
64 | * The estimated heap memory used by this object, in number of double words |
65 | * (4 bytes each). |
66 | */ |
67 | private final int memoryEstimated; |
68 | |
69 | PageData(PageDataIndex index, int pageId, Data data) { |
70 | this.index = index; |
71 | this.data = data; |
72 | setPos(pageId); |
73 | memoryEstimated = index.getMemoryPerPage(); |
74 | } |
75 | |
76 | /** |
77 | * Get the real row count. If required, this will read all child pages. |
78 | * |
79 | * @return the row count |
80 | */ |
81 | abstract int getRowCount(); |
82 | |
83 | /** |
84 | * Set the stored row count. This will write the page. |
85 | * |
86 | * @param rowCount the stored row count |
87 | */ |
88 | abstract void setRowCountStored(int rowCount); |
89 | |
90 | /** |
91 | * Get the used disk space for this index. |
92 | * |
93 | * @return the estimated number of bytes |
94 | */ |
95 | abstract long getDiskSpaceUsed(); |
96 | |
97 | /** |
98 | * Find an entry by key. |
99 | * |
100 | * @param key the key (may not exist) |
101 | * @return the matching or next index |
102 | */ |
103 | int find(long key) { |
104 | int l = 0, r = entryCount; |
105 | while (l < r) { |
106 | int i = (l + r) >>> 1; |
107 | long k = keys[i]; |
108 | if (k == key) { |
109 | return i; |
110 | } else if (k > key) { |
111 | r = i; |
112 | } else { |
113 | l = i + 1; |
114 | } |
115 | } |
116 | return l; |
117 | } |
118 | |
119 | /** |
120 | * Add a row if possible. If it is possible this method returns -1, |
121 | * otherwise the split point. It is always possible to add one row. |
122 | * |
123 | * @param row the now to add |
124 | * @return the split point of this page, or -1 if no split is required |
125 | */ |
126 | abstract int addRowTry(Row row); |
127 | |
128 | /** |
129 | * Get a cursor. |
130 | * |
131 | * @param session the session |
132 | * @param minKey the smallest key |
133 | * @param maxKey the largest key |
134 | * @param multiVersion if the delta should be used |
135 | * @return the cursor |
136 | */ |
137 | abstract Cursor find(Session session, long minKey, long maxKey, |
138 | boolean multiVersion); |
139 | |
140 | /** |
141 | * Get the key at this position. |
142 | * |
143 | * @param at the index |
144 | * @return the key |
145 | */ |
146 | long getKey(int at) { |
147 | return keys[at]; |
148 | } |
149 | |
150 | /** |
151 | * Split the index page at the given point. |
152 | * |
153 | * @param splitPoint the index where to split |
154 | * @return the new page that contains about half the entries |
155 | */ |
156 | abstract PageData split(int splitPoint); |
157 | |
158 | /** |
159 | * Change the page id. |
160 | * |
161 | * @param id the new page id |
162 | */ |
163 | void setPageId(int id) { |
164 | int old = getPos(); |
165 | index.getPageStore().removeFromCache(getPos()); |
166 | setPos(id); |
167 | index.getPageStore().logUndo(this, null); |
168 | remapChildren(old); |
169 | } |
170 | |
171 | /** |
172 | * Get the last key of a page. |
173 | * |
174 | * @return the last key |
175 | */ |
176 | abstract long getLastKey(); |
177 | |
178 | /** |
179 | * Get the first child leaf page of a page. |
180 | * |
181 | * @return the page |
182 | */ |
183 | abstract PageDataLeaf getFirstLeaf(); |
184 | |
185 | /** |
186 | * Change the parent page id. |
187 | * |
188 | * @param id the new parent page id |
189 | */ |
190 | void setParentPageId(int id) { |
191 | index.getPageStore().logUndo(this, data); |
192 | parentPageId = id; |
193 | if (written) { |
194 | changeCount = index.getPageStore().getChangeCount(); |
195 | data.setInt(START_PARENT, parentPageId); |
196 | } |
197 | } |
198 | |
199 | /** |
200 | * Update the parent id of all children. |
201 | * |
202 | * @param old the previous position |
203 | */ |
204 | abstract void remapChildren(int old); |
205 | |
206 | /** |
207 | * Remove a row. |
208 | * |
209 | * @param key the key of the row to remove |
210 | * @return true if this page is now empty |
211 | */ |
212 | abstract boolean remove(long key); |
213 | |
214 | /** |
215 | * Free this page and all child pages. |
216 | */ |
217 | abstract void freeRecursive(); |
218 | |
219 | /** |
220 | * Get the row for the given key. |
221 | * |
222 | * @param key the key |
223 | * @return the row |
224 | */ |
225 | abstract Row getRowWithKey(long key); |
226 | |
227 | /** |
228 | * Get the estimated heap memory size. |
229 | * |
230 | * @return number of double words (4 bytes each) |
231 | */ |
232 | @Override |
233 | public int getMemory() { |
234 | // need to always return the same value for the same object (otherwise |
235 | // the cache size would change after adding and then removing the same |
236 | // page from the cache) but index.getMemoryPerPage() can adopt according |
237 | // to how much memory a row needs on average |
238 | return memoryEstimated; |
239 | } |
240 | |
241 | int getParentPageId() { |
242 | return parentPageId; |
243 | } |
244 | |
245 | @Override |
246 | public boolean canRemove() { |
247 | if (changeCount >= index.getPageStore().getChangeCount()) { |
248 | return false; |
249 | } |
250 | return true; |
251 | } |
252 | |
253 | } |