1 5 package org.h2.index; 6 7 import java.sql.SQLException ; 8 9 import org.h2.engine.Constants; 10 import org.h2.engine.Database; 11 import org.h2.engine.Session; 12 import org.h2.message.Message; 13 import org.h2.result.Row; 14 import org.h2.result.SearchRow; 15 import org.h2.store.DataPage; 16 import org.h2.store.Record; 17 import org.h2.store.RecordReader; 18 import org.h2.store.Storage; 19 import org.h2.table.Column; 20 import org.h2.table.TableData; 21 import org.h2.util.ObjectArray; 22 import org.h2.value.Value; 23 import org.h2.value.ValueNull; 24 25 28 public class BtreeIndex extends Index implements RecordReader { 29 30 33 private Storage storage; 34 private BtreePage root; 35 private TableData tableData; 36 private BtreeHead head; 37 private boolean needRebuild; 38 private int headPos; 39 private long lastChange; 40 41 public BtreeIndex(Session session, TableData table, int id, String indexName, Column[] columns, 42 IndexType indexType, int headPos) throws SQLException { 43 super(table, id, indexName, columns, indexType); 45 this.tableData = table; 46 Database db = table.getDatabase(); 47 storage = db.getStorage(this, id, false); 48 this.headPos = headPos; 49 if (headPos == Index.EMPTY_HEAD || database.getRecovery()) { 50 truncate(session); 51 needRebuild = true; 52 } else { 53 Record rec = storage.getRecordIfStored(headPos); 54 if(rec != null && (rec instanceof BtreeHead)) { 55 head = (BtreeHead) rec; 56 } 57 if(head != null && head.getConsistent()) { 58 setRoot((BtreePage) storage.getRecord(head.getRootPosition())); 59 needRebuild = false; 60 rowCount = table.getRowCount(); 61 } else { 62 truncate(session); 63 needRebuild = true; 64 } 65 } 66 } 67 68 private void setRoot(BtreePage newRoot) { 69 if (root != null) { 70 root.setRoot(false); 71 } 72 newRoot.setRoot(true); 73 root = newRoot; 74 } 75 76 public int getHeadPos() { 77 return headPos; 78 } 79 80 public void remove(Session session) throws SQLException { 81 storage.delete(session); 82 storage = null; 83 } 84 85 private void setChanged(Session session) throws SQLException { 86 if(head != null && !database.getLogIndexChanges()) { 87 database.invalidateIndexSummary(); 89 if(head.getConsistent()) { 90 deletePage(session, head); 91 head.setConsistent(false); 92 flushHead(session); 93 } 94 lastChange = System.currentTimeMillis(); 95 } 96 } 97 98 void updatePage(Session session, Record p) throws SQLException { 99 if(database.getLogIndexChanges()) { 100 storage.addRecord(session, p, p.getPos()); 101 } else { 102 storage.updateRecord(session, p); 103 } 104 } 105 106 void deletePage(Session session, Record p) throws SQLException { 107 if(database.getLogIndexChanges()) { 108 storage.removeRecord(session, p.getPos()); 109 } 110 } 111 112 void addPage(Session session, Record p) throws SQLException { 113 storage.addRecord(session, p, Storage.ALLOCATE_POS); 114 } 115 116 public BtreePage getPage(int i) throws SQLException { 117 return (BtreePage) storage.getRecord(i); 118 } 119 120 public void flush(Session session) throws SQLException { 121 lastChange = 0; 122 if(storage != null) { 123 storage.flushFile(); 124 deletePage(session, head); 125 if(database.getLogIndexChanges() || !database.getLog().containsInDoubtTransactions()) { 128 head.setConsistent(true); 129 } 130 flushHead(session); 131 } 132 } 133 134 public void close(Session session) throws SQLException { 135 flush(session); 136 storage = null; 137 } 138 139 public void add(Session session, Row r) throws SQLException { 140 setChanged(session); 142 Row row = table.getTemplateRow(); 143 row.setPos(r.getPos()); 144 for (int i = 0; i < columns.length; i++) { 145 Column col = columns[i]; 146 int idx = col.getColumnId(); 147 Value v = r.getValue(idx); 148 row.setValue(idx, v); 149 } 150 int splitPoint = root.add(row, session); 151 if (splitPoint != 0) { 152 SearchRow pivot = root.getData(splitPoint); 153 BtreePage page1 = root; 154 BtreePage page2 = root.split(session, splitPoint); 155 setRoot(new BtreeNode(this, page1, pivot, page2)); 156 addPage(session, root); 157 deletePage(session, head); 158 head.setRootPosition(root.getPos()); 159 flushHead(session); 160 } 161 rowCount++; 162 } 163 164 public void remove(Session session, Row row) throws SQLException { 165 setChanged(session); 166 if(rowCount == 1) { 167 truncate(session); 169 } else { 170 root.remove(session, row, 0); 171 rowCount--; 172 } 173 } 174 175 public Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException { 176 if(Constants.CHECK && storage == null) { 177 throw Message.getSQLException(Message.OBJECT_CLOSED); 178 } 179 if(first==null) { 180 BtreeCursor cursor = new BtreeCursor(this, last); 181 root.first(cursor); 182 return cursor; 183 } else { 184 BtreeCursor cursor = new BtreeCursor(this, last); 185 if (!root.findFirst(cursor, first)) { 186 cursor.setCurrentRow(Cursor.POS_NO_ROW); 187 } 188 return cursor; 189 } 190 } 191 192 public int getLookupCost(int rowCount) { 193 for(int i=0, j = 1; ; i++) { 194 j *= BtreePage.BLOCKS_PER_PAGE; 195 if(j>rowCount) { 196 return (i+1); 197 } 198 } 199 } 200 201 public int getCost(int[] masks) throws SQLException { 202 return 10 * getCostRangeIndex(masks, tableData.getRowCount()); 203 } 204 205 public Record read(DataPage s) throws SQLException { 206 char c = (char) s.readByte(); 207 if (c == 'N') { 208 return new BtreeNode(this, s); 209 } else if (c == 'L') { 210 return new BtreeLeaf(this, s); 211 } else if (c == 'H') { 212 return new BtreeHead(s); 213 } else { 214 throw Message.getSQLException(Message.FILE_CORRUPTED_1, getName()); 215 } 216 } 217 218 ObjectArray readRowArray(DataPage s) throws SQLException { 219 int len = s.readInt(); 220 ObjectArray rows = new ObjectArray(len); 221 for (int i = 0; i < len; i++) { 222 int pos = s.readInt(); 223 SearchRow r; 224 if(pos < 0) { 225 r = null; 226 } else { 227 r = table.getTemplateSimpleRow(columns.length==1); 228 r.setPos(pos); 229 for (int j = 0; j < columns.length; j++) { 230 int idx = columns[j].getColumnId(); 231 r.setValue(idx, s.readValue()); 232 } 233 } 234 rows.add(r); 235 } 236 return rows; 237 } 238 239 public Row getRow(int pos) throws SQLException { 240 return tableData.getRow(pos); 241 } 242 243 private void flushHead(Session session) throws SQLException { 244 updatePage(session, head); 245 if(!database.getLogIndexChanges() && !database.getReadOnly()) { 246 storage.flushRecord(head); 247 } 248 trace.debug("Index " + getSQL() + " head consistent="+head.getConsistent()); 249 } 250 251 public void truncate(Session session) throws SQLException { 252 setChanged(session); 253 storage.truncate(session); 254 head = new BtreeHead(); 255 addPage(session, head); 256 setRoot(new BtreeLeaf(this, new ObjectArray())); 257 addPage(session, root); 258 deletePage(session, head); 259 head.setRootPosition(root.getPos()); 260 head.setConsistent(database.getLogIndexChanges()); 261 lastChange = System.currentTimeMillis(); 262 flushHead(session); 263 headPos = head.getPos(); 264 rowCount = 0; 265 } 266 267 public void checkRename() throws SQLException { 268 } 270 271 public boolean needRebuild() { 272 return needRebuild; 273 } 274 275 public int getRecordOverhead() { 276 return storage.getRecordOverhead(); 277 } 278 279 public long getLastChange() { 280 return lastChange; 281 } 282 283 public boolean canGetFirstOrLast(boolean first) { 284 return true; 285 } 286 287 public Value findFirstOrLast(Session session, boolean first) throws SQLException { 288 if(first) { 289 Cursor cursor = find(session, null, null); 291 while(cursor.next()) { 292 Value v = cursor.get().getValue(columnIndex[0]); 293 if(v != ValueNull.INSTANCE) { 294 return v; 295 } 296 } 297 return ValueNull.INSTANCE; 298 } else { 299 SearchRow row = root.getLast(); 300 if(row != null) { 301 Value v = row.getValue(columnIndex[0]); 302 return v; 303 } 304 return ValueNull.INSTANCE; 305 } 306 } 307 308 } 309 | Popular Tags |