You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377
  1. /*
  2. Copyright (c) 2005 Health Market Science, Inc.
  3. This library is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU Lesser General Public
  5. License as published by the Free Software Foundation; either
  6. version 2.1 of the License, or (at your option) any later version.
  7. This library is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  10. Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public
  12. License along with this library; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  14. USA
  15. You can contact Health Market Science at info@healthmarketscience.com
  16. or at the following address:
  17. Health Market Science
  18. 2700 Horizon Drive
  19. Suite 200
  20. King of Prussia, PA 19406
  21. */
  22. package com.healthmarketscience.jackcess;
  23. import java.io.IOException;
  24. import java.nio.ByteBuffer;
  25. import java.nio.ByteOrder;
  26. import java.util.ArrayList;
  27. import java.util.Arrays;
  28. import java.util.Collections;
  29. import java.util.Comparator;
  30. import java.util.List;
  31. import java.util.Map;
  32. import org.apache.commons.logging.Log;
  33. import org.apache.commons.logging.LogFactory;
  34. import static com.healthmarketscience.jackcess.IndexCodes.*;
  35. import static com.healthmarketscience.jackcess.ByteUtil.ByteStream;
  36. /**
  37. * Access table index data. This is the actual data which backs a logical
  38. * Index, where one or more logical indexes can be backed by the same index
  39. * data.
  40. *
  41. * @author Tim McCune
  42. */
  43. public abstract class IndexData {
  44. protected static final Log LOG = LogFactory.getLog(Index.class);
  45. /** special entry which is less than any other entry */
  46. public static final Entry FIRST_ENTRY =
  47. createSpecialEntry(RowId.FIRST_ROW_ID);
  48. /** special entry which is greater than any other entry */
  49. public static final Entry LAST_ENTRY =
  50. createSpecialEntry(RowId.LAST_ROW_ID);
  51. /** special object which will always be greater than any other value, when
  52. searching for an index entry range in a multi-value index */
  53. public static final Object MAX_VALUE = new Object();
  54. /** special object which will always be greater than any other value, when
  55. searching for an index entry range in a multi-value index */
  56. public static final Object MIN_VALUE = new Object();
  57. protected static final int INVALID_INDEX_PAGE_NUMBER = 0;
  58. /** Max number of columns in an index */
  59. static final int MAX_COLUMNS = 10;
  60. protected static final byte[] EMPTY_PREFIX = new byte[0];
  61. static final short COLUMN_UNUSED = -1;
  62. static final byte ASCENDING_COLUMN_FLAG = (byte)0x01;
  63. static final byte UNIQUE_INDEX_FLAG = (byte)0x01;
  64. static final byte IGNORE_NULLS_INDEX_FLAG = (byte)0x02;
  65. static final byte SPECIAL_INDEX_FLAG = (byte)0x08; // set on MSysACEs and MSysAccessObjects indexes, purpose unknown
  66. static final byte UNKNOWN_INDEX_FLAG = (byte)0x80; // always seems to be set on indexes in access 2000+
  67. private static final int MAGIC_INDEX_NUMBER = 1923;
  68. private static final ByteOrder ENTRY_BYTE_ORDER = ByteOrder.BIG_ENDIAN;
  69. /** type attributes for Entries which simplify comparisons */
  70. public enum EntryType {
  71. /** comparable type indicating this Entry should always compare less than
  72. valid RowIds */
  73. ALWAYS_FIRST,
  74. /** comparable type indicating this Entry should always compare less than
  75. other valid entries with equal entryBytes */
  76. FIRST_VALID,
  77. /** comparable type indicating this RowId should always compare
  78. normally */
  79. NORMAL,
  80. /** comparable type indicating this Entry should always compare greater
  81. than other valid entries with equal entryBytes */
  82. LAST_VALID,
  83. /** comparable type indicating this Entry should always compare greater
  84. than valid RowIds */
  85. ALWAYS_LAST;
  86. }
  87. static final Comparator<byte[]> BYTE_CODE_COMPARATOR =
  88. new Comparator<byte[]>() {
  89. public int compare(byte[] left, byte[] right) {
  90. if(left == right) {
  91. return 0;
  92. }
  93. if(left == null) {
  94. return -1;
  95. }
  96. if(right == null) {
  97. return 1;
  98. }
  99. int len = Math.min(left.length, right.length);
  100. int pos = 0;
  101. while((pos < len) && (left[pos] == right[pos])) {
  102. ++pos;
  103. }
  104. if(pos < len) {
  105. return ((ByteUtil.asUnsignedByte(left[pos]) <
  106. ByteUtil.asUnsignedByte(right[pos])) ? -1 : 1);
  107. }
  108. return ((left.length < right.length) ? -1 :
  109. ((left.length > right.length) ? 1 : 0));
  110. }
  111. };
  112. /** owning table */
  113. private final Table _table;
  114. /** 0-based index data number */
  115. private final int _number;
  116. /** Page number of the root index data */
  117. private int _rootPageNumber;
  118. /** offset within the tableDefinition buffer of the uniqueEntryCount for
  119. this index */
  120. private final int _uniqueEntryCountOffset;
  121. /** The number of unique entries which have been added to this index. note,
  122. however, that it is never decremented, only incremented (as observed in
  123. Access). */
  124. private int _uniqueEntryCount;
  125. /** List of columns and flags */
  126. private final List<ColumnDescriptor> _columns =
  127. new ArrayList<ColumnDescriptor>();
  128. /** the logical indexes which this index data backs */
  129. private final List<Index> _indexes = new ArrayList<Index>();
  130. /** flags for this index */
  131. private byte _indexFlags;
  132. /** Usage map of pages that this index owns */
  133. private UsageMap _ownedPages;
  134. /** <code>true</code> if the index entries have been initialized,
  135. <code>false</code> otherwise */
  136. private boolean _initialized;
  137. /** modification count for the table, keeps cursors up-to-date */
  138. private int _modCount;
  139. /** temp buffer used to read/write the index pages */
  140. private final TempBufferHolder _indexBufferH =
  141. TempBufferHolder.newHolder(TempBufferHolder.Type.SOFT, true);
  142. /** temp buffer used to create index entries */
  143. private ByteStream _entryBuffer;
  144. /** max size for all the entries written to a given index data page */
  145. private final int _maxPageEntrySize;
  146. /** whether or not this index data is backing a primary key logical index */
  147. private boolean _primaryKey;
  148. /** FIXME, for SimpleIndex, we can't write multi-page indexes or indexes using the entry compression scheme */
  149. private boolean _readOnly;
  150. protected IndexData(Table table, int number, int uniqueEntryCount,
  151. int uniqueEntryCountOffset)
  152. {
  153. _table = table;
  154. _number = number;
  155. _uniqueEntryCount = uniqueEntryCount;
  156. _uniqueEntryCountOffset = uniqueEntryCountOffset;
  157. _maxPageEntrySize = calcMaxPageEntrySize(_table.getFormat());
  158. }
  159. /**
  160. * Creates an IndexData appropriate for the given table, using information
  161. * from the given table definition buffer.
  162. */
  163. public static IndexData create(Table table, ByteBuffer tableBuffer,
  164. int number, JetFormat format)
  165. throws IOException
  166. {
  167. int uniqueEntryCountOffset =
  168. (format.OFFSET_INDEX_DEF_BLOCK +
  169. (number * format.SIZE_INDEX_DEFINITION) + 4);
  170. int uniqueEntryCount = tableBuffer.getInt(uniqueEntryCountOffset);
  171. return(table.doUseBigIndex() ?
  172. new BigIndexData(table, number, uniqueEntryCount,
  173. uniqueEntryCountOffset) :
  174. new SimpleIndexData(table, number, uniqueEntryCount,
  175. uniqueEntryCountOffset));
  176. }
  177. public Table getTable() {
  178. return _table;
  179. }
  180. public JetFormat getFormat() {
  181. return getTable().getFormat();
  182. }
  183. public PageChannel getPageChannel() {
  184. return getTable().getPageChannel();
  185. }
  186. /**
  187. * @return the "main" logical index which is backed by this data.
  188. */
  189. public Index getPrimaryIndex() {
  190. return _indexes.get(0);
  191. }
  192. /**
  193. * @return All of the Indexes backed by this data (unmodifiable List)
  194. */
  195. public List<Index> getIndexes() {
  196. return Collections.unmodifiableList(_indexes);
  197. }
  198. /**
  199. * Adds a logical index which this data is backing.
  200. */
  201. void addIndex(Index index) {
  202. // we keep foreign key indexes at the back of the list. this way the
  203. // primary index will be a non-foreign key index (if any)
  204. if(index.isForeignKey()) {
  205. _indexes.add(index);
  206. } else {
  207. int pos = _indexes.size();
  208. while(pos > 0) {
  209. if(!_indexes.get(pos - 1).isForeignKey()) {
  210. break;
  211. }
  212. --pos;
  213. }
  214. _indexes.add(pos, index);
  215. // also, keep track of whether or not this is a primary key index
  216. _primaryKey |= index.isPrimaryKey();
  217. }
  218. }
  219. public byte getIndexFlags() {
  220. return _indexFlags;
  221. }
  222. public int getIndexDataNumber() {
  223. return _number;
  224. }
  225. public int getUniqueEntryCount() {
  226. return _uniqueEntryCount;
  227. }
  228. public int getUniqueEntryCountOffset() {
  229. return _uniqueEntryCountOffset;
  230. }
  231. protected boolean isBackingPrimaryKey() {
  232. return _primaryKey;
  233. }
  234. /**
  235. * Whether or not {@code null} values are actually recorded in the index.
  236. */
  237. public boolean shouldIgnoreNulls() {
  238. return((_indexFlags & IGNORE_NULLS_INDEX_FLAG) != 0);
  239. }
  240. /**
  241. * Whether or not index entries must be unique.
  242. * <p>
  243. * Some notes about uniqueness:
  244. * <ul>
  245. * <li>Access does not seem to consider multiple {@code null} entries
  246. * invalid for a unique index</li>
  247. * <li>text indexes collapse case, and Access seems to compare <b>only</b>
  248. * the index entry bytes, therefore two strings which differ only in
  249. * case <i>will violate</i> the unique constraint</li>
  250. * </ul>
  251. */
  252. public boolean isUnique() {
  253. return(isBackingPrimaryKey() || ((_indexFlags & UNIQUE_INDEX_FLAG) != 0));
  254. }
  255. /**
  256. * Returns the Columns for this index (unmodifiable)
  257. */
  258. public List<ColumnDescriptor> getColumns() {
  259. return Collections.unmodifiableList(_columns);
  260. }
  261. /**
  262. * Whether or not the complete index state has been read.
  263. */
  264. public boolean isInitialized() {
  265. return _initialized;
  266. }
  267. protected int getRootPageNumber() {
  268. return _rootPageNumber;
  269. }
  270. protected void setReadOnly() {
  271. _readOnly = true;
  272. }
  273. protected boolean isReadOnly() {
  274. return _readOnly;
  275. }
  276. protected int getMaxPageEntrySize() {
  277. return _maxPageEntrySize;
  278. }
  279. /**
  280. * Returns the number of database pages owned by this index data.
  281. * @usage _intermediate_method_
  282. */
  283. public int getOwnedPageCount() {
  284. return _ownedPages.getPageCount();
  285. }
  286. void addOwnedPage(int pageNumber) throws IOException {
  287. _ownedPages.addPageNumber(pageNumber);
  288. }
  289. /**
  290. * Returns the number of index entries in the index. Only called by unit
  291. * tests.
  292. * <p>
  293. * Forces index initialization.
  294. */
  295. protected int getEntryCount()
  296. throws IOException
  297. {
  298. initialize();
  299. EntryCursor cursor = cursor();
  300. Entry endEntry = cursor.getLastEntry();
  301. int count = 0;
  302. while(!endEntry.equals(cursor.getNextEntry())) {
  303. ++count;
  304. }
  305. return count;
  306. }
  307. /**
  308. * Forces initialization of this index (actual parsing of index pages).
  309. * normally, the index will not be initialized until the entries are
  310. * actually needed.
  311. */
  312. public void initialize() throws IOException {
  313. if(!_initialized) {
  314. readIndexEntries();
  315. _initialized = true;
  316. }
  317. }
  318. /**
  319. * Writes the current index state to the database.
  320. * <p>
  321. * Forces index initialization.
  322. */
  323. public void update() throws IOException
  324. {
  325. // make sure we've parsed the entries
  326. initialize();
  327. if(_readOnly) {
  328. throw new UnsupportedOperationException(
  329. "FIXME cannot write indexes of this type yet, see Database javadoc for info on enabling large index support");
  330. }
  331. updateImpl();
  332. }
  333. /**
  334. * Read the rest of the index info from a tableBuffer
  335. * @param tableBuffer table definition buffer to read from initial info
  336. * @param availableColumns Columns that this index may use
  337. */
  338. public void read(ByteBuffer tableBuffer, List<Column> availableColumns)
  339. throws IOException
  340. {
  341. ByteUtil.forward(tableBuffer, getFormat().SKIP_BEFORE_INDEX); //Forward past Unknown
  342. for (int i = 0; i < MAX_COLUMNS; i++) {
  343. short columnNumber = tableBuffer.getShort();
  344. byte colFlags = tableBuffer.get();
  345. if (columnNumber != COLUMN_UNUSED) {
  346. // find the desired column by column number (which is not necessarily
  347. // the same as the column index)
  348. Column idxCol = null;
  349. for(Column col : availableColumns) {
  350. if(col.getColumnNumber() == columnNumber) {
  351. idxCol = col;
  352. break;
  353. }
  354. }
  355. if(idxCol == null) {
  356. throw new IOException("Could not find column with number "
  357. + columnNumber + " for index");
  358. }
  359. _columns.add(newColumnDescriptor(idxCol, colFlags));
  360. }
  361. }
  362. _ownedPages = UsageMap.read(getTable().getDatabase(), tableBuffer, false);
  363. _rootPageNumber = tableBuffer.getInt();
  364. ByteUtil.forward(tableBuffer, getFormat().SKIP_BEFORE_INDEX_FLAGS); //Forward past Unknown
  365. _indexFlags = tableBuffer.get();
  366. ByteUtil.forward(tableBuffer, getFormat().SKIP_AFTER_INDEX_FLAGS); //Forward past other stuff
  367. }
  368. /**
  369. * Writes the index row count definitions into a table definition buffer.
  370. * @param buffer Buffer to write to
  371. * @param indexes List of IndexBuilders to write definitions for
  372. */
  373. protected static void writeRowCountDefinitions(
  374. TableCreator creator, ByteBuffer buffer)
  375. {
  376. // index row counts (empty data)
  377. ByteUtil.forward(buffer, (creator.getIndexCount() *
  378. creator.getFormat().SIZE_INDEX_DEFINITION));
  379. }
  380. /**
  381. * Writes the index definitions into a table definition buffer.
  382. * @param buffer Buffer to write to
  383. * @param indexes List of IndexBuilders to write definitions for
  384. */
  385. protected static void writeDefinitions(
  386. TableCreator creator, ByteBuffer buffer)
  387. throws IOException
  388. {
  389. ByteBuffer rootPageBuffer = creator.getPageChannel().createPageBuffer();
  390. writeDataPage(rootPageBuffer, SimpleIndexData.NEW_ROOT_DATA_PAGE,
  391. creator.getTdefPageNumber(), creator.getFormat());
  392. for(IndexBuilder idx : creator.getIndexes()) {
  393. buffer.putInt(MAGIC_INDEX_NUMBER); // seemingly constant magic value
  394. // write column information (always MAX_COLUMNS entries)
  395. List<IndexBuilder.Column> idxColumns = idx.getColumns();
  396. for(int i = 0; i < MAX_COLUMNS; ++i) {
  397. short columnNumber = COLUMN_UNUSED;
  398. byte flags = 0;
  399. if(i < idxColumns.size()) {
  400. // determine column info
  401. IndexBuilder.Column idxCol = idxColumns.get(i);
  402. flags = idxCol.getFlags();
  403. // find actual table column number
  404. for(Column col : creator.getColumns()) {
  405. if(col.getName().equalsIgnoreCase(idxCol.getName())) {
  406. columnNumber = col.getColumnNumber();
  407. break;
  408. }
  409. }
  410. if(columnNumber == COLUMN_UNUSED) {
  411. // should never happen as this is validated before
  412. throw new IllegalArgumentException(
  413. "Column with name " + idxCol.getName() + " not found");
  414. }
  415. }
  416. buffer.putShort(columnNumber); // table column number
  417. buffer.put(flags); // column flags (e.g. ordering)
  418. }
  419. TableCreator.IndexState idxState = creator.getIndexState(idx);
  420. buffer.put(idxState.getUmapRowNumber()); // umap row
  421. ByteUtil.put3ByteInt(buffer, creator.getUmapPageNumber()); // umap page
  422. // write empty root index page
  423. creator.getPageChannel().writePage(rootPageBuffer,
  424. idxState.getRootPageNumber());
  425. buffer.putInt(idxState.getRootPageNumber());
  426. buffer.putInt(0); // unknown
  427. buffer.put(idx.getFlags()); // index flags (unique, etc.)
  428. ByteUtil.forward(buffer, 5); // unknown
  429. }
  430. }
  431. /**
  432. * Adds a row to this index
  433. * <p>
  434. * Forces index initialization.
  435. *
  436. * @param row Row to add
  437. * @param rowId rowId of the row to be added
  438. */
  439. public void addRow(Object[] row, RowId rowId)
  440. throws IOException
  441. {
  442. int nullCount = countNullValues(row);
  443. boolean isNullEntry = (nullCount == _columns.size());
  444. if(shouldIgnoreNulls() && isNullEntry) {
  445. // nothing to do
  446. return;
  447. }
  448. if(isBackingPrimaryKey() && (nullCount > 0)) {
  449. throw new IOException("Null value found in row " + Arrays.asList(row)
  450. + " for primary key index " + this);
  451. }
  452. // make sure we've parsed the entries
  453. initialize();
  454. Entry newEntry = new Entry(createEntryBytes(row), rowId);
  455. if(addEntry(newEntry, isNullEntry, row)) {
  456. ++_modCount;
  457. } else {
  458. LOG.warn("Added duplicate index entry " + newEntry + " for row: " +
  459. Arrays.asList(row));
  460. }
  461. }
  462. /**
  463. * Adds an entry to the correct index dataPage, maintaining the order.
  464. */
  465. private boolean addEntry(Entry newEntry, boolean isNullEntry, Object[] row)
  466. throws IOException
  467. {
  468. DataPage dataPage = findDataPage(newEntry);
  469. int idx = dataPage.findEntry(newEntry);
  470. if(idx < 0) {
  471. // this is a new entry
  472. idx = missingIndexToInsertionPoint(idx);
  473. Position newPos = new Position(dataPage, idx, newEntry, true);
  474. Position nextPos = getNextPosition(newPos);
  475. Position prevPos = getPreviousPosition(newPos);
  476. // determine if the addition of this entry would break the uniqueness
  477. // constraint. See isUnique() for some notes about uniqueness as
  478. // defined by Access.
  479. boolean isDupeEntry =
  480. (((nextPos != null) &&
  481. newEntry.equalsEntryBytes(nextPos.getEntry())) ||
  482. ((prevPos != null) &&
  483. newEntry.equalsEntryBytes(prevPos.getEntry())));
  484. if(isUnique() && !isNullEntry && isDupeEntry) {
  485. throw new IOException(
  486. "New row " + Arrays.asList(row) +
  487. " violates uniqueness constraint for index " + this);
  488. }
  489. if(!isDupeEntry) {
  490. ++_uniqueEntryCount;
  491. }
  492. dataPage.addEntry(idx, newEntry);
  493. return true;
  494. }
  495. return false;
  496. }
  497. /**
  498. * Removes a row from this index
  499. * <p>
  500. * Forces index initialization.
  501. *
  502. * @param row Row to remove
  503. * @param rowId rowId of the row to be removed
  504. */
  505. public void deleteRow(Object[] row, RowId rowId)
  506. throws IOException
  507. {
  508. int nullCount = countNullValues(row);
  509. if(shouldIgnoreNulls() && (nullCount == _columns.size())) {
  510. // nothing to do
  511. return;
  512. }
  513. // make sure we've parsed the entries
  514. initialize();
  515. Entry oldEntry = new Entry(createEntryBytes(row), rowId);
  516. if(removeEntry(oldEntry)) {
  517. ++_modCount;
  518. } else {
  519. LOG.warn("Failed removing index entry " + oldEntry + " for row: " +
  520. Arrays.asList(row));
  521. }
  522. }
  523. /**
  524. * Removes an entry from the relevant index dataPage, maintaining the order.
  525. * Will search by RowId if entry is not found (in case a partial entry was
  526. * provided).
  527. */
  528. private boolean removeEntry(Entry oldEntry)
  529. throws IOException
  530. {
  531. DataPage dataPage = findDataPage(oldEntry);
  532. int idx = dataPage.findEntry(oldEntry);
  533. boolean doRemove = false;
  534. if(idx < 0) {
  535. // the caller may have only read some of the row data, if this is the
  536. // case, just search for the page/row numbers
  537. // FIXME, we could force caller to get relevant values?
  538. EntryCursor cursor = cursor();
  539. Position tmpPos = null;
  540. Position endPos = cursor._lastPos;
  541. while(!endPos.equals(
  542. tmpPos = cursor.getAnotherPosition(Cursor.MOVE_FORWARD))) {
  543. if(tmpPos.getEntry().getRowId().equals(oldEntry.getRowId())) {
  544. dataPage = tmpPos.getDataPage();
  545. idx = tmpPos.getIndex();
  546. doRemove = true;
  547. break;
  548. }
  549. }
  550. } else {
  551. doRemove = true;
  552. }
  553. if(doRemove) {
  554. // found it!
  555. dataPage.removeEntry(idx);
  556. }
  557. return doRemove;
  558. }
  559. /**
  560. * Gets a new cursor for this index.
  561. * <p>
  562. * Forces index initialization.
  563. */
  564. public EntryCursor cursor()
  565. throws IOException
  566. {
  567. return cursor(null, true, null, true);
  568. }
  569. /**
  570. * Gets a new cursor for this index, narrowed to the range defined by the
  571. * given startRow and endRow.
  572. * <p>
  573. * Forces index initialization.
  574. *
  575. * @param startRow the first row of data for the cursor, or {@code null} for
  576. * the first entry
  577. * @param startInclusive whether or not startRow is inclusive or exclusive
  578. * @param endRow the last row of data for the cursor, or {@code null} for
  579. * the last entry
  580. * @param endInclusive whether or not endRow is inclusive or exclusive
  581. */
  582. public EntryCursor cursor(Object[] startRow,
  583. boolean startInclusive,
  584. Object[] endRow,
  585. boolean endInclusive)
  586. throws IOException
  587. {
  588. initialize();
  589. Entry startEntry = FIRST_ENTRY;
  590. byte[] startEntryBytes = null;
  591. if(startRow != null) {
  592. startEntryBytes = createEntryBytes(startRow);
  593. startEntry = new Entry(startEntryBytes,
  594. (startInclusive ?
  595. RowId.FIRST_ROW_ID : RowId.LAST_ROW_ID));
  596. }
  597. Entry endEntry = LAST_ENTRY;
  598. if(endRow != null) {
  599. // reuse startEntryBytes if startRow and endRow are same array. this is
  600. // common for "lookup" code
  601. byte[] endEntryBytes = ((startRow == endRow) ?
  602. startEntryBytes :
  603. createEntryBytes(endRow));
  604. endEntry = new Entry(endEntryBytes,
  605. (endInclusive ?
  606. RowId.LAST_ROW_ID : RowId.FIRST_ROW_ID));
  607. }
  608. return new EntryCursor(findEntryPosition(startEntry),
  609. findEntryPosition(endEntry));
  610. }
  611. private Position findEntryPosition(Entry entry)
  612. throws IOException
  613. {
  614. DataPage dataPage = findDataPage(entry);
  615. int idx = dataPage.findEntry(entry);
  616. boolean between = false;
  617. if(idx < 0) {
  618. // given entry was not found exactly. our current position is now
  619. // really between two indexes, but we cannot support that as an integer
  620. // value, so we set a flag instead
  621. idx = missingIndexToInsertionPoint(idx);
  622. between = true;
  623. }
  624. return new Position(dataPage, idx, entry, between);
  625. }
  626. private Position getNextPosition(Position curPos)
  627. throws IOException
  628. {
  629. // get the next index (between-ness is handled internally)
  630. int nextIdx = curPos.getNextIndex();
  631. Position nextPos = null;
  632. if(nextIdx < curPos.getDataPage().getEntries().size()) {
  633. nextPos = new Position(curPos.getDataPage(), nextIdx);
  634. } else {
  635. int nextPageNumber = curPos.getDataPage().getNextPageNumber();
  636. DataPage nextDataPage = null;
  637. while(nextPageNumber != INVALID_INDEX_PAGE_NUMBER) {
  638. DataPage dp = getDataPage(nextPageNumber);
  639. if(!dp.isEmpty()) {
  640. nextDataPage = dp;
  641. break;
  642. }
  643. nextPageNumber = dp.getNextPageNumber();
  644. }
  645. if(nextDataPage != null) {
  646. nextPos = new Position(nextDataPage, 0);
  647. }
  648. }
  649. return nextPos;
  650. }
  651. /**
  652. * Returns the Position before the given one, or {@code null} if none.
  653. */
  654. private Position getPreviousPosition(Position curPos)
  655. throws IOException
  656. {
  657. // get the previous index (between-ness is handled internally)
  658. int prevIdx = curPos.getPrevIndex();
  659. Position prevPos = null;
  660. if(prevIdx >= 0) {
  661. prevPos = new Position(curPos.getDataPage(), prevIdx);
  662. } else {
  663. int prevPageNumber = curPos.getDataPage().getPrevPageNumber();
  664. DataPage prevDataPage = null;
  665. while(prevPageNumber != INVALID_INDEX_PAGE_NUMBER) {
  666. DataPage dp = getDataPage(prevPageNumber);
  667. if(!dp.isEmpty()) {
  668. prevDataPage = dp;
  669. break;
  670. }
  671. prevPageNumber = dp.getPrevPageNumber();
  672. }
  673. if(prevDataPage != null) {
  674. prevPos = new Position(prevDataPage,
  675. (prevDataPage.getEntries().size() - 1));
  676. }
  677. }
  678. return prevPos;
  679. }
  680. /**
  681. * Returns the valid insertion point for an index indicating a missing
  682. * entry.
  683. */
  684. protected static int missingIndexToInsertionPoint(int idx) {
  685. return -(idx + 1);
  686. }
  687. /**
  688. * Constructs an array of values appropriate for this index from the given
  689. * column values, expected to match the columns for this index.
  690. * @return the appropriate sparse array of data
  691. * @throws IllegalArgumentException if the wrong number of values are
  692. * provided
  693. */
  694. public Object[] constructIndexRowFromEntry(Object... values)
  695. {
  696. if(values.length != _columns.size()) {
  697. throw new IllegalArgumentException(
  698. "Wrong number of column values given " + values.length +
  699. ", expected " + _columns.size());
  700. }
  701. int valIdx = 0;
  702. Object[] idxRow = new Object[getTable().getColumnCount()];
  703. for(ColumnDescriptor col : _columns) {
  704. idxRow[col.getColumnIndex()] = values[valIdx++];
  705. }
  706. return idxRow;
  707. }
  708. /**
  709. * Constructs an array of values appropriate for this index from the given
  710. * column value.
  711. * @return the appropriate sparse array of data or {@code null} if not all
  712. * columns for this index were provided
  713. */
  714. public Object[] constructIndexRow(String colName, Object value)
  715. {
  716. return constructIndexRow(Collections.singletonMap(colName, value));
  717. }
  718. /**
  719. * Constructs an array of values appropriate for this index from the given
  720. * column values.
  721. * @return the appropriate sparse array of data or {@code null} if not all
  722. * columns for this index were provided
  723. */
  724. public Object[] constructIndexRow(Map<String,?> row)
  725. {
  726. for(ColumnDescriptor col : _columns) {
  727. if(!row.containsKey(col.getName())) {
  728. return null;
  729. }
  730. }
  731. Object[] idxRow = new Object[getTable().getColumnCount()];
  732. for(ColumnDescriptor col : _columns) {
  733. idxRow[col.getColumnIndex()] = row.get(col.getName());
  734. }
  735. return idxRow;
  736. }
  737. @Override
  738. public String toString() {
  739. StringBuilder rtn = new StringBuilder();
  740. rtn.append("\n\tData number: ").append(_number);
  741. rtn.append("\n\tPage number: ").append(_rootPageNumber);
  742. rtn.append("\n\tIs Backing Primary Key: ").append(isBackingPrimaryKey());
  743. rtn.append("\n\tIs Unique: ").append(isUnique());
  744. rtn.append("\n\tIgnore Nulls: ").append(shouldIgnoreNulls());
  745. rtn.append("\n\tColumns: ").append(_columns);
  746. rtn.append("\n\tInitialized: ").append(_initialized);
  747. if(_initialized) {
  748. try {
  749. rtn.append("\n\tEntryCount: ").append(getEntryCount());
  750. } catch(IOException e) {
  751. throw new RuntimeException(e);
  752. }
  753. }
  754. return rtn.toString();
  755. }
  756. /**
  757. * Write the given index page out to a buffer
  758. */
  759. protected void writeDataPage(DataPage dataPage)
  760. throws IOException
  761. {
  762. if(dataPage.getCompressedEntrySize() > _maxPageEntrySize) {
  763. if(this instanceof SimpleIndexData) {
  764. throw new UnsupportedOperationException(
  765. "FIXME cannot write large index yet, see Database javadoc for info on enabling large index support");
  766. }
  767. throw new IllegalStateException("data page is too large");
  768. }
  769. ByteBuffer buffer = _indexBufferH.getPageBuffer(getPageChannel());
  770. writeDataPage(buffer, dataPage, getTable().getTableDefPageNumber(),
  771. getFormat());
  772. getPageChannel().writePage(buffer, dataPage.getPageNumber());
  773. }
  774. /**
  775. * Writes the data page info to the given buffer.
  776. */
  777. protected static void writeDataPage(ByteBuffer buffer, DataPage dataPage,
  778. int tdefPageNumber, JetFormat format)
  779. throws IOException
  780. {
  781. buffer.put(dataPage.isLeaf() ?
  782. PageTypes.INDEX_LEAF :
  783. PageTypes.INDEX_NODE ); //Page type
  784. buffer.put((byte) 0x01); //Unknown
  785. buffer.putShort((short) 0); //Free space
  786. buffer.putInt(tdefPageNumber);
  787. buffer.putInt(0); //Unknown
  788. buffer.putInt(dataPage.getPrevPageNumber()); //Prev page
  789. buffer.putInt(dataPage.getNextPageNumber()); //Next page
  790. buffer.putInt(dataPage.getChildTailPageNumber()); //ChildTail page
  791. byte[] entryPrefix = dataPage.getEntryPrefix();
  792. buffer.putShort((short) entryPrefix.length); // entry prefix byte count
  793. buffer.put((byte) 0); //Unknown
  794. byte[] entryMask = new byte[format.SIZE_INDEX_ENTRY_MASK];
  795. // first entry includes the prefix
  796. int totalSize = entryPrefix.length;
  797. for(Entry entry : dataPage.getEntries()) {
  798. totalSize += (entry.size() - entryPrefix.length);
  799. int idx = totalSize / 8;
  800. entryMask[idx] |= (1 << (totalSize % 8));
  801. }
  802. buffer.put(entryMask);
  803. // first entry includes the prefix
  804. buffer.put(entryPrefix);
  805. for(Entry entry : dataPage.getEntries()) {
  806. entry.write(buffer, entryPrefix);
  807. }
  808. // update free space
  809. buffer.putShort(2, (short) (format.PAGE_SIZE - buffer.position()));
  810. }
  811. /**
  812. * Reads an index page, populating the correct collection based on the page
  813. * type (node or leaf).
  814. */
  815. protected void readDataPage(DataPage dataPage)
  816. throws IOException
  817. {
  818. ByteBuffer buffer = _indexBufferH.getPageBuffer(getPageChannel());
  819. getPageChannel().readPage(buffer, dataPage.getPageNumber());
  820. boolean isLeaf = isLeafPage(buffer);
  821. dataPage.setLeaf(isLeaf);
  822. // note, "header" data is in LITTLE_ENDIAN format, entry data is in
  823. // BIG_ENDIAN format
  824. int entryPrefixLength = ByteUtil.getUnsignedShort(
  825. buffer, getFormat().OFFSET_INDEX_COMPRESSED_BYTE_COUNT);
  826. int entryMaskLength = getFormat().SIZE_INDEX_ENTRY_MASK;
  827. int entryMaskPos = getFormat().OFFSET_INDEX_ENTRY_MASK;
  828. int entryPos = entryMaskPos + entryMaskLength;
  829. int lastStart = 0;
  830. int totalEntrySize = 0;
  831. byte[] entryPrefix = null;
  832. List<Entry> entries = new ArrayList<Entry>();
  833. TempBufferHolder tmpEntryBufferH =
  834. TempBufferHolder.newHolder(TempBufferHolder.Type.HARD, true,
  835. ENTRY_BYTE_ORDER);
  836. Entry prevEntry = FIRST_ENTRY;
  837. for (int i = 0; i < entryMaskLength; i++) {
  838. byte entryMask = buffer.get(entryMaskPos + i);
  839. for (int j = 0; j < 8; j++) {
  840. if ((entryMask & (1 << j)) != 0) {
  841. int length = (i * 8) + j - lastStart;
  842. buffer.position(entryPos + lastStart);
  843. // determine if we can read straight from the index page (if no
  844. // entryPrefix). otherwise, create temp buf with complete entry.
  845. ByteBuffer curEntryBuffer = buffer;
  846. int curEntryLen = length;
  847. if(entryPrefix != null) {
  848. curEntryBuffer = getTempEntryBuffer(
  849. buffer, length, entryPrefix, tmpEntryBufferH);
  850. curEntryLen += entryPrefix.length;
  851. }
  852. totalEntrySize += curEntryLen;
  853. Entry entry = newEntry(curEntryBuffer, curEntryLen, isLeaf);
  854. if(prevEntry.compareTo(entry) >= 0) {
  855. throw new IOException("Unexpected order in index entries, " +
  856. prevEntry + " >= " + entry);
  857. }
  858. entries.add(entry);
  859. if((entries.size() == 1) && (entryPrefixLength > 0)) {
  860. // read any shared entry prefix
  861. entryPrefix = new byte[entryPrefixLength];
  862. buffer.position(entryPos + lastStart);
  863. buffer.get(entryPrefix);
  864. }
  865. lastStart += length;
  866. prevEntry = entry;
  867. }
  868. }
  869. }
  870. dataPage.setEntryPrefix(entryPrefix != null ? entryPrefix : EMPTY_PREFIX);
  871. dataPage.setEntries(entries);
  872. dataPage.setTotalEntrySize(totalEntrySize);
  873. int prevPageNumber = buffer.getInt(getFormat().OFFSET_PREV_INDEX_PAGE);
  874. int nextPageNumber = buffer.getInt(getFormat().OFFSET_NEXT_INDEX_PAGE);
  875. int childTailPageNumber =
  876. buffer.getInt(getFormat().OFFSET_CHILD_TAIL_INDEX_PAGE);
  877. dataPage.setPrevPageNumber(prevPageNumber);
  878. dataPage.setNextPageNumber(nextPageNumber);
  879. dataPage.setChildTailPageNumber(childTailPageNumber);
  880. }
  881. /**
  882. * Returns a new Entry of the correct type for the given data and page type.
  883. */
  884. private static Entry newEntry(ByteBuffer buffer, int entryLength,
  885. boolean isLeaf)
  886. throws IOException
  887. {
  888. if(isLeaf) {
  889. return new Entry(buffer, entryLength);
  890. }
  891. return new NodeEntry(buffer, entryLength);
  892. }
  893. /**
  894. * Returns an entry buffer containing the relevant data for an entry given
  895. * the valuePrefix.
  896. */
  897. private ByteBuffer getTempEntryBuffer(
  898. ByteBuffer indexPage, int entryLen, byte[] valuePrefix,
  899. TempBufferHolder tmpEntryBufferH)
  900. {
  901. ByteBuffer tmpEntryBuffer = tmpEntryBufferH.getBuffer(
  902. getPageChannel(), valuePrefix.length + entryLen);
  903. // combine valuePrefix and rest of entry from indexPage, then prep for
  904. // reading
  905. tmpEntryBuffer.put(valuePrefix);
  906. tmpEntryBuffer.put(indexPage.array(), indexPage.position(), entryLen);
  907. tmpEntryBuffer.flip();
  908. return tmpEntryBuffer;
  909. }
  910. /**
  911. * Determines if the given index page is a leaf or node page.
  912. */
  913. private static boolean isLeafPage(ByteBuffer buffer)
  914. throws IOException
  915. {
  916. byte pageType = buffer.get(0);
  917. if(pageType == PageTypes.INDEX_LEAF) {
  918. return true;
  919. } else if(pageType == PageTypes.INDEX_NODE) {
  920. return false;
  921. }
  922. throw new IOException("Unexpected page type " + pageType);
  923. }
  924. /**
  925. * Determines the number of {@code null} values for this index from the
  926. * given row.
  927. */
  928. private int countNullValues(Object[] values)
  929. {
  930. if(values == null) {
  931. return _columns.size();
  932. }
  933. // annoyingly, the values array could come from different sources, one
  934. // of which will make it a different size than the other. we need to
  935. // handle both situations.
  936. int nullCount = 0;
  937. for(ColumnDescriptor col : _columns) {
  938. Object value = values[col.getColumnIndex()];
  939. if(col.isNullValue(value)) {
  940. ++nullCount;
  941. }
  942. }
  943. return nullCount;
  944. }
  945. /**
  946. * Creates the entry bytes for a row of values.
  947. */
  948. private byte[] createEntryBytes(Object[] values) throws IOException
  949. {
  950. if(values == null) {
  951. return null;
  952. }
  953. if(_entryBuffer == null) {
  954. _entryBuffer = new ByteStream();
  955. }
  956. _entryBuffer.reset();
  957. for(ColumnDescriptor col : _columns) {
  958. Object value = values[col.getColumnIndex()];
  959. if(Column.isRawData(value)) {
  960. // ignore it, we could not parse it
  961. continue;
  962. }
  963. if(value == MIN_VALUE) {
  964. // null is the "least" value
  965. _entryBuffer.write(getNullEntryFlag(col.isAscending()));
  966. continue;
  967. }
  968. if(value == MAX_VALUE) {
  969. // the opposite null is the "greatest" value
  970. _entryBuffer.write(getNullEntryFlag(!col.isAscending()));
  971. continue;
  972. }
  973. col.writeValue(value, _entryBuffer);
  974. }
  975. return _entryBuffer.toByteArray();
  976. }
  977. /**
  978. * Writes the current index state to the database. Index has already been
  979. * initialized.
  980. */
  981. protected abstract void updateImpl() throws IOException;
  982. /**
  983. * Reads the actual index entries.
  984. */
  985. protected abstract void readIndexEntries()
  986. throws IOException;
  987. /**
  988. * Finds the data page for the given entry.
  989. */
  990. protected abstract DataPage findDataPage(Entry entry)
  991. throws IOException;
  992. /**
  993. * Gets the data page for the pageNumber.
  994. */
  995. protected abstract DataPage getDataPage(int pageNumber)
  996. throws IOException;
  997. /**
  998. * Flips the first bit in the byte at the given index.
  999. */
  1000. private static byte[] flipFirstBitInByte(byte[] value, int index)
  1001. {
  1002. value[index] = (byte)(value[index] ^ 0x80);
  1003. return value;
  1004. }
  1005. /**
  1006. * Flips all the bits in the byte array.
  1007. */
  1008. private static byte[] flipBytes(byte[] value) {
  1009. return flipBytes(value, 0, value.length);
  1010. }
  1011. /**
  1012. * Flips the bits in the specified bytes in the byte array.
  1013. */
  1014. static byte[] flipBytes(byte[] value, int offset, int length) {
  1015. for(int i = offset; i < (offset + length); ++i) {
  1016. value[i] = (byte)(~value[i]);
  1017. }
  1018. return value;
  1019. }
  1020. /**
  1021. * Writes the value of the given column type to a byte array and returns it.
  1022. */
  1023. private static byte[] encodeNumberColumnValue(Object value, Column column)
  1024. throws IOException
  1025. {
  1026. // always write in big endian order
  1027. return column.write(value, 0, ENTRY_BYTE_ORDER).array();
  1028. }
  1029. /**
  1030. * Creates one of the special index entries.
  1031. */
  1032. private static Entry createSpecialEntry(RowId rowId) {
  1033. return new Entry((byte[])null, rowId);
  1034. }
  1035. /**
  1036. * Constructs a ColumnDescriptor of the relevant type for the given Column.
  1037. */
  1038. private ColumnDescriptor newColumnDescriptor(Column col, byte flags)
  1039. throws IOException
  1040. {
  1041. switch(col.getType()) {
  1042. case TEXT:
  1043. case MEMO:
  1044. Column.SortOrder sortOrder = col.getTextSortOrder();
  1045. if(Column.GENERAL_LEGACY_SORT_ORDER.equals(sortOrder)) {
  1046. return new GenLegTextColumnDescriptor(col, flags);
  1047. }
  1048. if(Column.GENERAL_SORT_ORDER.equals(sortOrder)) {
  1049. return new GenTextColumnDescriptor(col, flags);
  1050. }
  1051. // unsupported sort order
  1052. LOG.warn("Unsupported collating sort order " + sortOrder +
  1053. " for text index, making read-only");
  1054. setReadOnly();
  1055. return new ReadOnlyColumnDescriptor(col, flags);
  1056. case INT:
  1057. case LONG:
  1058. case MONEY:
  1059. case COMPLEX_TYPE:
  1060. return new IntegerColumnDescriptor(col, flags);
  1061. case FLOAT:
  1062. case DOUBLE:
  1063. case SHORT_DATE_TIME:
  1064. return new FloatingPointColumnDescriptor(col, flags);
  1065. case NUMERIC:
  1066. return (col.getFormat().LEGACY_NUMERIC_INDEXES ?
  1067. new LegacyFixedPointColumnDescriptor(col, flags) :
  1068. new FixedPointColumnDescriptor(col, flags));
  1069. case BYTE:
  1070. return new ByteColumnDescriptor(col, flags);
  1071. case BOOLEAN:
  1072. return new BooleanColumnDescriptor(col, flags);
  1073. case GUID:
  1074. return new GuidColumnDescriptor(col, flags);
  1075. default:
  1076. // FIXME we can't modify this index at this point in time
  1077. LOG.warn("Unsupported data type " + col.getType() +
  1078. " for index, making read-only");
  1079. setReadOnly();
  1080. return new ReadOnlyColumnDescriptor(col, flags);
  1081. }
  1082. }
  1083. /**
  1084. * Returns the EntryType based on the given entry info.
  1085. */
  1086. private static EntryType determineEntryType(byte[] entryBytes, RowId rowId)
  1087. {
  1088. if(entryBytes != null) {
  1089. return ((rowId.getType() == RowId.Type.NORMAL) ?
  1090. EntryType.NORMAL :
  1091. ((rowId.getType() == RowId.Type.ALWAYS_FIRST) ?
  1092. EntryType.FIRST_VALID : EntryType.LAST_VALID));
  1093. } else if(!rowId.isValid()) {
  1094. // this is a "special" entry (first/last)
  1095. return ((rowId.getType() == RowId.Type.ALWAYS_FIRST) ?
  1096. EntryType.ALWAYS_FIRST : EntryType.ALWAYS_LAST);
  1097. }
  1098. throw new IllegalArgumentException("Values was null for valid entry");
  1099. }
  1100. /**
  1101. * Returns the maximum amount of entry data which can be encoded on any
  1102. * index page.
  1103. */
  1104. private static int calcMaxPageEntrySize(JetFormat format)
  1105. {
  1106. // the max data we can fit on a page is the min of the space on the page
  1107. // vs the number of bytes which can be encoded in the entry mask
  1108. int pageDataSize = (format.PAGE_SIZE -
  1109. (format.OFFSET_INDEX_ENTRY_MASK +
  1110. format.SIZE_INDEX_ENTRY_MASK));
  1111. int entryMaskSize = (format.SIZE_INDEX_ENTRY_MASK * 8);
  1112. return Math.min(pageDataSize, entryMaskSize);
  1113. }
  1114. /**
  1115. * Information about the columns in an index. Also encodes new index
  1116. * values.
  1117. */
  1118. public static abstract class ColumnDescriptor
  1119. {
  1120. private final Column _column;
  1121. private final byte _flags;
  1122. private ColumnDescriptor(Column column, byte flags)
  1123. throws IOException
  1124. {
  1125. _column = column;
  1126. _flags = flags;
  1127. }
  1128. public Column getColumn() {
  1129. return _column;
  1130. }
  1131. public byte getFlags() {
  1132. return _flags;
  1133. }
  1134. public boolean isAscending() {
  1135. return((getFlags() & ASCENDING_COLUMN_FLAG) != 0);
  1136. }
  1137. public int getColumnIndex() {
  1138. return getColumn().getColumnIndex();
  1139. }
  1140. public String getName() {
  1141. return getColumn().getName();
  1142. }
  1143. protected boolean isNullValue(Object value) {
  1144. return (value == null);
  1145. }
  1146. protected final void writeValue(Object value, ByteStream bout)
  1147. throws IOException
  1148. {
  1149. if(isNullValue(value)) {
  1150. // write null value
  1151. bout.write(getNullEntryFlag(isAscending()));
  1152. return;
  1153. }
  1154. // write the start flag
  1155. bout.write(getStartEntryFlag(isAscending()));
  1156. // write the rest of the value
  1157. writeNonNullValue(value, bout);
  1158. }
  1159. protected abstract void writeNonNullValue(
  1160. Object value, ByteStream bout)
  1161. throws IOException;
  1162. @Override
  1163. public String toString() {
  1164. return "ColumnDescriptor " + getColumn() + "\nflags: " + getFlags();
  1165. }
  1166. }
  1167. /**
  1168. * ColumnDescriptor for integer based columns.
  1169. */
  1170. private static final class IntegerColumnDescriptor extends ColumnDescriptor
  1171. {
  1172. private IntegerColumnDescriptor(Column column, byte flags)
  1173. throws IOException
  1174. {
  1175. super(column, flags);
  1176. }
  1177. @Override
  1178. protected void writeNonNullValue(
  1179. Object value, ByteStream bout)
  1180. throws IOException
  1181. {
  1182. byte[] valueBytes = encodeNumberColumnValue(value, getColumn());
  1183. // bit twiddling rules:
  1184. // - isAsc => flipFirstBit
  1185. // - !isAsc => flipFirstBit, flipBytes
  1186. flipFirstBitInByte(valueBytes, 0);
  1187. if(!isAscending()) {
  1188. flipBytes(valueBytes);
  1189. }
  1190. bout.write(valueBytes);
  1191. }
  1192. }
  1193. /**
  1194. * ColumnDescriptor for floating point based columns.
  1195. */
  1196. private static final class FloatingPointColumnDescriptor
  1197. extends ColumnDescriptor
  1198. {
  1199. private FloatingPointColumnDescriptor(Column column, byte flags)
  1200. throws IOException
  1201. {
  1202. super(column, flags);
  1203. }
  1204. @Override
  1205. protected void writeNonNullValue(
  1206. Object value, ByteStream bout)
  1207. throws IOException
  1208. {
  1209. byte[] valueBytes = encodeNumberColumnValue(value, getColumn());
  1210. // determine if the number is negative by testing if the first bit is
  1211. // set
  1212. boolean isNegative = ((valueBytes[0] & 0x80) != 0);
  1213. // bit twiddling rules:
  1214. // isAsc && !isNeg => flipFirstBit
  1215. // isAsc && isNeg => flipBytes
  1216. // !isAsc && !isNeg => flipFirstBit, flipBytes
  1217. // !isAsc && isNeg => nothing
  1218. if(!isNegative) {
  1219. flipFirstBitInByte(valueBytes, 0);
  1220. }
  1221. if(isNegative == isAscending()) {
  1222. flipBytes(valueBytes);
  1223. }
  1224. bout.write(valueBytes);
  1225. }
  1226. }
  1227. /**
  1228. * ColumnDescriptor for fixed point based columns (legacy sort order).
  1229. */
  1230. private static class LegacyFixedPointColumnDescriptor
  1231. extends ColumnDescriptor
  1232. {
  1233. private LegacyFixedPointColumnDescriptor(Column column, byte flags)
  1234. throws IOException
  1235. {
  1236. super(column, flags);
  1237. }
  1238. protected void handleNegationAndOrder(boolean isNegative,
  1239. byte[] valueBytes)
  1240. {
  1241. if(isNegative == isAscending()) {
  1242. flipBytes(valueBytes);
  1243. }
  1244. // reverse the sign byte (after any previous byte flipping)
  1245. valueBytes[0] = (isNegative ? (byte)0x00 : (byte)0xFF);
  1246. }
  1247. @Override
  1248. protected void writeNonNullValue(
  1249. Object value, ByteStream bout)
  1250. throws IOException
  1251. {
  1252. byte[] valueBytes = encodeNumberColumnValue(value, getColumn());
  1253. // determine if the number is negative by testing if the first bit is
  1254. // set
  1255. boolean isNegative = ((valueBytes[0] & 0x80) != 0);
  1256. // bit twiddling rules:
  1257. // isAsc && !isNeg => setReverseSignByte => FF 00 00 ...
  1258. // isAsc && isNeg => flipBytes, setReverseSignByte => 00 FF FF ...
  1259. // !isAsc && !isNeg => flipBytes, setReverseSignByte => FF FF FF ...
  1260. // !isAsc && isNeg => setReverseSignByte => 00 00 00 ...
  1261. // v2007 bit twiddling rules (old ordering was a bug, MS kb 837148):
  1262. // isAsc && !isNeg => setSignByte 0xFF => FF 00 00 ...
  1263. // isAsc && isNeg => setSignByte 0xFF, flipBytes => 00 FF FF ...
  1264. // !isAsc && !isNeg => setSignByte 0xFF => FF 00 00 ...
  1265. // !isAsc && isNeg => setSignByte 0xFF, flipBytes => 00 FF FF ...
  1266. handleNegationAndOrder(isNegative, valueBytes);
  1267. bout.write(valueBytes);
  1268. }
  1269. }
  1270. /**
  1271. * ColumnDescriptor for new-style fixed point based columns.
  1272. */
  1273. private static final class FixedPointColumnDescriptor
  1274. extends LegacyFixedPointColumnDescriptor
  1275. {
  1276. private FixedPointColumnDescriptor(Column column, byte flags)
  1277. throws IOException
  1278. {
  1279. super(column, flags);
  1280. }
  1281. @Override
  1282. protected void handleNegationAndOrder(boolean isNegative,
  1283. byte[] valueBytes)
  1284. {
  1285. // see notes above in FixedPointColumnDescriptor for bit twiddling rules
  1286. // reverse the sign byte (before any byte flipping)
  1287. valueBytes[0] = (byte)0xFF;
  1288. if(isNegative == isAscending()) {
  1289. flipBytes(valueBytes);
  1290. }
  1291. }
  1292. }
  1293. /**
  1294. * ColumnDescriptor for byte based columns.
  1295. */
  1296. private static final class ByteColumnDescriptor extends ColumnDescriptor
  1297. {
  1298. private ByteColumnDescriptor(Column column, byte flags)
  1299. throws IOException
  1300. {
  1301. super(column, flags);
  1302. }
  1303. @Override
  1304. protected void writeNonNullValue(
  1305. Object value, ByteStream bout)
  1306. throws IOException
  1307. {
  1308. byte[] valueBytes = encodeNumberColumnValue(value, getColumn());
  1309. // bit twiddling rules:
  1310. // - isAsc => nothing
  1311. // - !isAsc => flipBytes
  1312. if(!isAscending()) {
  1313. flipBytes(valueBytes);
  1314. }
  1315. bout.write(valueBytes);
  1316. }
  1317. }
  1318. /**
  1319. * ColumnDescriptor for boolean columns.
  1320. */
  1321. private static final class BooleanColumnDescriptor extends ColumnDescriptor
  1322. {
  1323. private BooleanColumnDescriptor(Column column, byte flags)
  1324. throws IOException
  1325. {
  1326. super(column, flags);
  1327. }
  1328. @Override
  1329. protected boolean isNullValue(Object value) {
  1330. // null values are handled as booleans
  1331. return false;
  1332. }
  1333. @Override
  1334. protected void writeNonNullValue(Object value, ByteStream bout)
  1335. throws IOException
  1336. {
  1337. bout.write(
  1338. Column.toBooleanValue(value) ?
  1339. (isAscending() ? ASC_BOOLEAN_TRUE : DESC_BOOLEAN_TRUE) :
  1340. (isAscending() ? ASC_BOOLEAN_FALSE : DESC_BOOLEAN_FALSE));
  1341. }
  1342. }
  1343. /**
  1344. * ColumnDescriptor for "general legacy" sort order text based columns.
  1345. */
  1346. private static final class GenLegTextColumnDescriptor
  1347. extends ColumnDescriptor
  1348. {
  1349. private GenLegTextColumnDescriptor(Column column, byte flags)
  1350. throws IOException
  1351. {
  1352. super(column, flags);
  1353. }
  1354. @Override
  1355. protected void writeNonNullValue(
  1356. Object value, ByteStream bout)
  1357. throws IOException
  1358. {
  1359. GeneralLegacyIndexCodes.GEN_LEG_INSTANCE.writeNonNullIndexTextValue(
  1360. value, bout, isAscending());
  1361. }
  1362. }
  1363. /**
  1364. * ColumnDescriptor for "general" sort order (2010+) text based columns.
  1365. */
  1366. private static final class GenTextColumnDescriptor extends ColumnDescriptor
  1367. {
  1368. private GenTextColumnDescriptor(Column column, byte flags)
  1369. throws IOException
  1370. {
  1371. super(column, flags);
  1372. }
  1373. @Override
  1374. protected void writeNonNullValue(
  1375. Object value, ByteStream bout)
  1376. throws IOException
  1377. {
  1378. GeneralIndexCodes.GEN_INSTANCE.writeNonNullIndexTextValue(
  1379. value, bout, isAscending());
  1380. }
  1381. }
  1382. /**
  1383. * ColumnDescriptor for guid columns.
  1384. */
  1385. private static final class GuidColumnDescriptor extends ColumnDescriptor
  1386. {
  1387. private GuidColumnDescriptor(Column column, byte flags)
  1388. throws IOException
  1389. {
  1390. super(column, flags);
  1391. }
  1392. @Override
  1393. protected void writeNonNullValue(
  1394. Object value, ByteStream bout)
  1395. throws IOException
  1396. {
  1397. byte[] valueBytes = encodeNumberColumnValue(value, getColumn());
  1398. // index format <8-bytes> 0x09 <8-bytes> 0x08
  1399. // bit twiddling rules:
  1400. // - isAsc => nothing
  1401. // - !isAsc => flipBytes, _but keep 09 unflipped_!
  1402. if(!isAscending()) {
  1403. flipBytes(valueBytes);
  1404. }
  1405. bout.write(valueBytes, 0, 8);
  1406. bout.write(MID_GUID);
  1407. bout.write(valueBytes, 8, 8);
  1408. bout.write(isAscending() ? ASC_END_GUID : DESC_END_GUID);
  1409. }
  1410. }
  1411. /**
  1412. * ColumnDescriptor for columns which we cannot currently write.
  1413. */
  1414. private static final class ReadOnlyColumnDescriptor extends ColumnDescriptor
  1415. {
  1416. private ReadOnlyColumnDescriptor(Column column, byte flags)
  1417. throws IOException
  1418. {
  1419. super(column, flags);
  1420. }
  1421. @Override
  1422. protected void writeNonNullValue(Object value, ByteStream bout)
  1423. throws IOException
  1424. {
  1425. throw new UnsupportedOperationException("should not be called");
  1426. }
  1427. }
  1428. /**
  1429. * A single leaf entry in an index (points to a single row)
  1430. */
  1431. public static class Entry implements Comparable<Entry>
  1432. {
  1433. /** page/row on which this row is stored */
  1434. private final RowId _rowId;
  1435. /** the entry value */
  1436. private final byte[] _entryBytes;
  1437. /** comparable type for the entry */
  1438. private final EntryType _type;
  1439. /**
  1440. * Create a new entry
  1441. * @param entryBytes encoded bytes for this index entry
  1442. * @param rowId rowId in which the row is stored
  1443. * @param type the type of the entry
  1444. */
  1445. private Entry(byte[] entryBytes, RowId rowId, EntryType type) {
  1446. _rowId = rowId;
  1447. _entryBytes = entryBytes;
  1448. _type = type;
  1449. }
  1450. /**
  1451. * Create a new entry
  1452. * @param entryBytes encoded bytes for this index entry
  1453. * @param rowId rowId in which the row is stored
  1454. */
  1455. private Entry(byte[] entryBytes, RowId rowId)
  1456. {
  1457. this(entryBytes, rowId, determineEntryType(entryBytes, rowId));
  1458. }
  1459. /**
  1460. * Read an existing entry in from a buffer
  1461. */
  1462. private Entry(ByteBuffer buffer, int entryLen)
  1463. throws IOException
  1464. {
  1465. this(buffer, entryLen, 0);
  1466. }
  1467. /**
  1468. * Read an existing entry in from a buffer
  1469. */
  1470. private Entry(ByteBuffer buffer, int entryLen, int extraTrailingLen)
  1471. throws IOException
  1472. {
  1473. // we need 4 trailing bytes for the rowId, plus whatever the caller
  1474. // wants
  1475. int colEntryLen = entryLen - (4 + extraTrailingLen);
  1476. // read the entry bytes
  1477. _entryBytes = ByteUtil.getBytes(buffer, colEntryLen);
  1478. // read the rowId
  1479. int page = ByteUtil.get3ByteInt(buffer, ENTRY_BYTE_ORDER);
  1480. int row = ByteUtil.getUnsignedByte(buffer);
  1481. _rowId = new RowId(page, row);
  1482. _type = EntryType.NORMAL;
  1483. }
  1484. public RowId getRowId() {
  1485. return _rowId;
  1486. }
  1487. public EntryType getType() {
  1488. return _type;
  1489. }
  1490. public Integer getSubPageNumber() {
  1491. throw new UnsupportedOperationException();
  1492. }
  1493. public boolean isLeafEntry() {
  1494. return true;
  1495. }
  1496. public boolean isValid() {
  1497. return(_entryBytes != null);
  1498. }
  1499. protected final byte[] getEntryBytes() {
  1500. return _entryBytes;
  1501. }
  1502. /**
  1503. * Size of this entry in the db.
  1504. */
  1505. protected int size() {
  1506. // need 4 trailing bytes for the rowId
  1507. return _entryBytes.length + 4;
  1508. }
  1509. /**
  1510. * Write this entry into a buffer
  1511. */
  1512. protected void write(ByteBuffer buffer,
  1513. byte[] prefix)
  1514. throws IOException
  1515. {
  1516. if(prefix.length <= _entryBytes.length) {
  1517. // write entry bytes, not including prefix
  1518. buffer.put(_entryBytes, prefix.length,
  1519. (_entryBytes.length - prefix.length));
  1520. ByteUtil.put3ByteInt(buffer, getRowId().getPageNumber(),
  1521. ENTRY_BYTE_ORDER);
  1522. } else if(prefix.length <= (_entryBytes.length + 3)) {
  1523. // the prefix includes part of the page number, write to temp buffer
  1524. // and copy last bytes to output buffer
  1525. ByteBuffer tmp = ByteBuffer.allocate(3);
  1526. ByteUtil.put3ByteInt(tmp, getRowId().getPageNumber(),
  1527. ENTRY_BYTE_ORDER);
  1528. tmp.flip();
  1529. tmp.position(prefix.length - _entryBytes.length);
  1530. buffer.put(tmp);
  1531. } else {
  1532. // since the row number would never be the same if the page number is
  1533. // the same, nothing past the page number should ever be included in
  1534. // the prefix.
  1535. // FIXME, this could happen if page has only one row...
  1536. throw new IllegalStateException("prefix should never be this long");
  1537. }
  1538. buffer.put((byte)getRowId().getRowNumber());
  1539. }
  1540. protected final String entryBytesToString() {
  1541. return (isValid() ? ", Bytes = " + ByteUtil.toHexString(
  1542. ByteBuffer.wrap(_entryBytes), _entryBytes.length) :
  1543. "");
  1544. }
  1545. @Override
  1546. public String toString() {
  1547. return "RowId = " + _rowId + entryBytesToString() + "\n";
  1548. }
  1549. @Override
  1550. public int hashCode() {
  1551. return _rowId.hashCode();
  1552. }
  1553. @Override
  1554. public boolean equals(Object o) {
  1555. return((this == o) ||
  1556. ((o != null) && (getClass() == o.getClass()) &&
  1557. (compareTo((Entry)o) == 0)));
  1558. }
  1559. /**
  1560. * @return {@code true} iff the entryBytes are equal between this
  1561. * Entry and the given Entry
  1562. */
  1563. public boolean equalsEntryBytes(Entry o) {
  1564. return(BYTE_CODE_COMPARATOR.compare(_entryBytes, o._entryBytes) == 0);
  1565. }
  1566. public int compareTo(Entry other) {
  1567. if (this == other) {
  1568. return 0;
  1569. }
  1570. if(isValid() && other.isValid()) {
  1571. // comparing two valid entries. first, compare by actual byte values
  1572. int entryCmp = BYTE_CODE_COMPARATOR.compare(
  1573. _entryBytes, other._entryBytes);
  1574. if(entryCmp != 0) {
  1575. return entryCmp;
  1576. }
  1577. } else {
  1578. // if the entries are of mixed validity (or both invalid), we defer
  1579. // next to the EntryType
  1580. int typeCmp = _type.compareTo(other._type);
  1581. if(typeCmp != 0) {
  1582. return typeCmp;
  1583. }
  1584. }
  1585. // at this point we let the RowId decide the final result
  1586. return _rowId.compareTo(other.getRowId());
  1587. }
  1588. /**
  1589. * Returns a copy of this entry as a node Entry with the given
  1590. * subPageNumber.
  1591. */
  1592. protected Entry asNodeEntry(Integer subPageNumber) {
  1593. return new NodeEntry(_entryBytes, _rowId, _type, subPageNumber);
  1594. }
  1595. }
  1596. /**
  1597. * A single node entry in an index (points to a sub-page in the index)
  1598. */
  1599. private static final class NodeEntry extends Entry {
  1600. /** index page number of the page to which this node entry refers */
  1601. private final Integer _subPageNumber;
  1602. /**
  1603. * Create a new node entry
  1604. * @param entryBytes encoded bytes for this index entry
  1605. * @param rowId rowId in which the row is stored
  1606. * @param type the type of the entry
  1607. * @param subPageNumber the sub-page to which this node entry refers
  1608. */
  1609. private NodeEntry(byte[] entryBytes, RowId rowId, EntryType type,
  1610. Integer subPageNumber) {
  1611. super(entryBytes, rowId, type);
  1612. _subPageNumber = subPageNumber;
  1613. }
  1614. /**
  1615. * Read an existing node entry in from a buffer
  1616. */
  1617. private NodeEntry(ByteBuffer buffer, int entryLen)
  1618. throws IOException
  1619. {
  1620. // we need 4 trailing bytes for the sub-page number
  1621. super(buffer, entryLen, 4);
  1622. _subPageNumber = ByteUtil.getInt(buffer, ENTRY_BYTE_ORDER);
  1623. }
  1624. @Override
  1625. public Integer getSubPageNumber() {
  1626. return _subPageNumber;
  1627. }
  1628. @Override
  1629. public boolean isLeafEntry() {
  1630. return false;
  1631. }
  1632. @Override
  1633. protected int size() {
  1634. // need 4 trailing bytes for the sub-page number
  1635. return super.size() + 4;
  1636. }
  1637. @Override
  1638. protected void write(ByteBuffer buffer, byte[] prefix) throws IOException {
  1639. super.write(buffer, prefix);
  1640. ByteUtil.putInt(buffer, _subPageNumber, ENTRY_BYTE_ORDER);
  1641. }
  1642. @Override
  1643. public boolean equals(Object o) {
  1644. return((this == o) ||
  1645. ((o != null) && (getClass() == o.getClass()) &&
  1646. (compareTo((Entry)o) == 0) &&
  1647. (getSubPageNumber().equals(((Entry)o).getSubPageNumber()))));
  1648. }
  1649. @Override
  1650. public String toString() {
  1651. return ("Node RowId = " + getRowId() +
  1652. ", SubPage = " + _subPageNumber + entryBytesToString() + "\n");
  1653. }
  1654. }
  1655. /**
  1656. * Utility class to traverse the entries in the Index. Remains valid in the
  1657. * face of index entry modifications.
  1658. */
  1659. public final class EntryCursor
  1660. {
  1661. /** handler for moving the page cursor forward */
  1662. private final DirHandler _forwardDirHandler = new ForwardDirHandler();
  1663. /** handler for moving the page cursor backward */
  1664. private final DirHandler _reverseDirHandler = new ReverseDirHandler();
  1665. /** the first (exclusive) row id for this cursor */
  1666. private Position _firstPos;
  1667. /** the last (exclusive) row id for this cursor */
  1668. private Position _lastPos;
  1669. /** the current entry */
  1670. private Position _curPos;
  1671. /** the previous entry */
  1672. private Position _prevPos;
  1673. /** the last read modification count on the Index. we track this so that
  1674. the cursor can detect updates to the index while traversing and act
  1675. accordingly */
  1676. private int _lastModCount;
  1677. private EntryCursor(Position firstPos, Position lastPos)
  1678. {
  1679. _firstPos = firstPos;
  1680. _lastPos = lastPos;
  1681. _lastModCount = getIndexModCount();
  1682. reset();
  1683. }
  1684. /**
  1685. * Returns the DirHandler for the given direction
  1686. */
  1687. private DirHandler getDirHandler(boolean moveForward) {
  1688. return (moveForward ? _forwardDirHandler : _reverseDirHandler);
  1689. }
  1690. public IndexData getIndexData() {
  1691. return IndexData.this;
  1692. }
  1693. private int getIndexModCount() {
  1694. return IndexData.this._modCount;
  1695. }
  1696. /**
  1697. * Returns the first entry (exclusive) as defined by this cursor.
  1698. */
  1699. public Entry getFirstEntry() {
  1700. return _firstPos.getEntry();
  1701. }
  1702. /**
  1703. * Returns the last entry (exclusive) as defined by this cursor.
  1704. */
  1705. public Entry getLastEntry() {
  1706. return _lastPos.getEntry();
  1707. }
  1708. /**
  1709. * Returns {@code true} if this cursor is up-to-date with respect to its
  1710. * index.
  1711. */
  1712. public boolean isUpToDate() {
  1713. return(getIndexModCount() == _lastModCount);
  1714. }
  1715. public void reset() {
  1716. beforeFirst();
  1717. }
  1718. public void beforeFirst() {
  1719. reset(Cursor.MOVE_FORWARD);
  1720. }
  1721. public void afterLast() {
  1722. reset(Cursor.MOVE_REVERSE);
  1723. }
  1724. protected void reset(boolean moveForward)
  1725. {
  1726. _curPos = getDirHandler(moveForward).getBeginningPosition();
  1727. _prevPos = _curPos;
  1728. }
  1729. /**
  1730. * Repositions the cursor so that the next row will be the first entry
  1731. * >= the given row.
  1732. */
  1733. public void beforeEntry(Object[] row)
  1734. throws IOException
  1735. {
  1736. restorePosition(
  1737. new Entry(IndexData.this.createEntryBytes(row), RowId.FIRST_ROW_ID));
  1738. }
  1739. /**
  1740. * Repositions the cursor so that the previous row will be the first
  1741. * entry <= the given row.
  1742. */
  1743. public void afterEntry(Object[] row)
  1744. throws IOException
  1745. {
  1746. restorePosition(
  1747. new Entry(IndexData.this.createEntryBytes(row), RowId.LAST_ROW_ID));
  1748. }
  1749. /**
  1750. * @return valid entry if there was a next entry,
  1751. * {@code #getLastEntry} otherwise
  1752. */
  1753. public Entry getNextEntry() throws IOException {
  1754. return getAnotherPosition(Cursor.MOVE_FORWARD).getEntry();
  1755. }
  1756. /**
  1757. * @return valid entry if there was a next entry,
  1758. * {@code #getFirstEntry} otherwise
  1759. */
  1760. public Entry getPreviousEntry() throws IOException {
  1761. return getAnotherPosition(Cursor.MOVE_REVERSE).getEntry();
  1762. }
  1763. /**
  1764. * Restores a current position for the cursor (current position becomes
  1765. * previous position).
  1766. */
  1767. protected void restorePosition(Entry curEntry)
  1768. throws IOException
  1769. {
  1770. restorePosition(curEntry, _curPos.getEntry());
  1771. }
  1772. /**
  1773. * Restores a current and previous position for the cursor.
  1774. */
  1775. protected void restorePosition(Entry curEntry, Entry prevEntry)
  1776. throws IOException
  1777. {
  1778. if(!_curPos.equalsEntry(curEntry) ||
  1779. !_prevPos.equalsEntry(prevEntry))
  1780. {
  1781. if(!isUpToDate()) {
  1782. updateBounds();
  1783. _lastModCount = getIndexModCount();
  1784. }
  1785. _prevPos = updatePosition(prevEntry);
  1786. _curPos = updatePosition(curEntry);
  1787. } else {
  1788. checkForModification();
  1789. }
  1790. }
  1791. /**
  1792. * Gets another entry in the given direction, returning the new entry.
  1793. */
  1794. private Position getAnotherPosition(boolean moveForward)
  1795. throws IOException
  1796. {
  1797. DirHandler handler = getDirHandler(moveForward);
  1798. if(_curPos.equals(handler.getEndPosition())) {
  1799. if(!isUpToDate()) {
  1800. restorePosition(_prevPos.getEntry());
  1801. // drop through and retry moving to another entry
  1802. } else {
  1803. // at end, no more
  1804. return _curPos;
  1805. }
  1806. }
  1807. checkForModification();
  1808. _prevPos = _curPos;
  1809. _curPos = handler.getAnotherPosition(_curPos);
  1810. return _curPos;
  1811. }
  1812. /**
  1813. * Checks the index for modifications and updates state accordingly.
  1814. */
  1815. private void checkForModification()
  1816. throws IOException
  1817. {
  1818. if(!isUpToDate()) {
  1819. updateBounds();
  1820. _prevPos = updatePosition(_prevPos.getEntry());
  1821. _curPos = updatePosition(_curPos.getEntry());
  1822. _lastModCount = getIndexModCount();
  1823. }
  1824. }
  1825. /**
  1826. * Updates the given position, taking boundaries into account.
  1827. */
  1828. private Position updatePosition(Entry entry)
  1829. throws IOException
  1830. {
  1831. if(!entry.isValid()) {
  1832. // no use searching if "updating" the first/last pos
  1833. if(_firstPos.equalsEntry(entry)) {
  1834. return _firstPos;
  1835. } else if(_lastPos.equalsEntry(entry)) {
  1836. return _lastPos;
  1837. } else {
  1838. throw new IllegalArgumentException("Invalid entry given " + entry);
  1839. }
  1840. }
  1841. Position pos = findEntryPosition(entry);
  1842. if(pos.compareTo(_lastPos) >= 0) {
  1843. return _lastPos;
  1844. } else if(pos.compareTo(_firstPos) <= 0) {
  1845. return _firstPos;
  1846. }
  1847. return pos;
  1848. }
  1849. /**
  1850. * Updates any the boundary info (_firstPos/_lastPos).
  1851. */
  1852. private void updateBounds()
  1853. throws IOException
  1854. {
  1855. _firstPos = findEntryPosition(_firstPos.getEntry());
  1856. _lastPos = findEntryPosition(_lastPos.getEntry());
  1857. }
  1858. @Override
  1859. public String toString() {
  1860. return getClass().getSimpleName() + " CurPosition " + _curPos +
  1861. ", PrevPosition " + _prevPos;
  1862. }
  1863. /**
  1864. * Handles moving the cursor in a given direction. Separates cursor
  1865. * logic from value storage.
  1866. */
  1867. private abstract class DirHandler {
  1868. public abstract Position getAnotherPosition(Position curPos)
  1869. throws IOException;
  1870. public abstract Position getBeginningPosition();
  1871. public abstract Position getEndPosition();
  1872. }
  1873. /**
  1874. * Handles moving the cursor forward.
  1875. */
  1876. private final class ForwardDirHandler extends DirHandler {
  1877. @Override
  1878. public Position getAnotherPosition(Position curPos)
  1879. throws IOException
  1880. {
  1881. Position newPos = getNextPosition(curPos);
  1882. if((newPos == null) || (newPos.compareTo(_lastPos) >= 0)) {
  1883. newPos = _lastPos;
  1884. }
  1885. return newPos;
  1886. }
  1887. @Override
  1888. public Position getBeginningPosition() {
  1889. return _firstPos;
  1890. }
  1891. @Override
  1892. public Position getEndPosition() {
  1893. return _lastPos;
  1894. }
  1895. }
  1896. /**
  1897. * Handles moving the cursor backward.
  1898. */
  1899. private final class ReverseDirHandler extends DirHandler {
  1900. @Override
  1901. public Position getAnotherPosition(Position curPos)
  1902. throws IOException
  1903. {
  1904. Position newPos = getPreviousPosition(curPos);
  1905. if((newPos == null) || (newPos.compareTo(_firstPos) <= 0)) {
  1906. newPos = _firstPos;
  1907. }
  1908. return newPos;
  1909. }
  1910. @Override
  1911. public Position getBeginningPosition() {
  1912. return _lastPos;
  1913. }
  1914. @Override
  1915. public Position getEndPosition() {
  1916. return _firstPos;
  1917. }
  1918. }
  1919. }
  1920. /**
  1921. * Simple value object for maintaining some cursor state.
  1922. */
  1923. private static final class Position implements Comparable<Position> {
  1924. /** the last known page of the given entry */
  1925. private final DataPage _dataPage;
  1926. /** the last known index of the given entry */
  1927. private final int _idx;
  1928. /** the entry at the given index */
  1929. private final Entry _entry;
  1930. /** {@code true} if this entry does not currently exist in the entry list,
  1931. {@code false} otherwise (this is equivalent to adding -0.5 to the
  1932. _idx) */
  1933. private final boolean _between;
  1934. private Position(DataPage dataPage, int idx)
  1935. {
  1936. this(dataPage, idx, dataPage.getEntries().get(idx), false);
  1937. }
  1938. private Position(DataPage dataPage, int idx, Entry entry, boolean between)
  1939. {
  1940. _dataPage = dataPage;
  1941. _idx = idx;
  1942. _entry = entry;
  1943. _between = between;
  1944. }
  1945. public DataPage getDataPage() {
  1946. return _dataPage;
  1947. }
  1948. public int getIndex() {
  1949. return _idx;
  1950. }
  1951. public int getNextIndex() {
  1952. // note, _idx does not need to be advanced if it was pointing at a
  1953. // between position
  1954. return(_between ? _idx : (_idx + 1));
  1955. }
  1956. public int getPrevIndex() {
  1957. // note, we ignore the between flag here because the index will be
  1958. // pointing at the correct next index in either the between or
  1959. // non-between case
  1960. return(_idx - 1);
  1961. }
  1962. public Entry getEntry() {
  1963. return _entry;
  1964. }
  1965. public boolean isBetween() {
  1966. return _between;
  1967. }
  1968. public boolean equalsEntry(Entry entry) {
  1969. return _entry.equals(entry);
  1970. }
  1971. public int compareTo(Position other)
  1972. {
  1973. if(this == other) {
  1974. return 0;
  1975. }
  1976. if(_dataPage.equals(other._dataPage)) {
  1977. // "simple" index comparison (handle between-ness)
  1978. int idxCmp = ((_idx < other._idx) ? -1 :
  1979. ((_idx > other._idx) ? 1 :
  1980. ((_between == other._between) ? 0 :
  1981. (_between ? -1 : 1))));
  1982. if(idxCmp != 0) {
  1983. return idxCmp;
  1984. }
  1985. }
  1986. // compare the entries.
  1987. return _entry.compareTo(other._entry);
  1988. }
  1989. @Override
  1990. public int hashCode() {
  1991. return _entry.hashCode();
  1992. }
  1993. @Override
  1994. public boolean equals(Object o) {
  1995. return((this == o) ||
  1996. ((o != null) && (getClass() == o.getClass()) &&
  1997. (compareTo((Position)o) == 0)));
  1998. }
  1999. @Override
  2000. public String toString() {
  2001. return "Page = " + _dataPage.getPageNumber() + ", Idx = " + _idx +
  2002. ", Entry = " + _entry + ", Between = " + _between;
  2003. }
  2004. }
  2005. /**
  2006. * Object used to maintain state about an Index page.
  2007. */
  2008. protected static abstract class DataPage {
  2009. public abstract int getPageNumber();
  2010. public abstract boolean isLeaf();
  2011. public abstract void setLeaf(boolean isLeaf);
  2012. public abstract int getPrevPageNumber();
  2013. public abstract void setPrevPageNumber(int pageNumber);
  2014. public abstract int getNextPageNumber();
  2015. public abstract void setNextPageNumber(int pageNumber);
  2016. public abstract int getChildTailPageNumber();
  2017. public abstract void setChildTailPageNumber(int pageNumber);
  2018. public abstract int getTotalEntrySize();
  2019. public abstract void setTotalEntrySize(int totalSize);
  2020. public abstract byte[] getEntryPrefix();
  2021. public abstract void setEntryPrefix(byte[] entryPrefix);
  2022. public abstract List<Entry> getEntries();
  2023. public abstract void setEntries(List<Entry> entries);
  2024. public abstract void addEntry(int idx, Entry entry)
  2025. throws IOException;
  2026. public abstract void removeEntry(int idx)
  2027. throws IOException;
  2028. public final boolean isEmpty() {
  2029. return getEntries().isEmpty();
  2030. }
  2031. public final int getCompressedEntrySize() {
  2032. // when written to the index page, the entryPrefix bytes will only be
  2033. // written for the first entry, so we subtract the entry prefix size
  2034. // from all the other entries to determine the compressed size
  2035. return getTotalEntrySize() -
  2036. (getEntryPrefix().length * (getEntries().size() - 1));
  2037. }
  2038. public final int findEntry(Entry entry) {
  2039. return Collections.binarySearch(getEntries(), entry);
  2040. }
  2041. @Override
  2042. public final int hashCode() {
  2043. return getPageNumber();
  2044. }
  2045. @Override
  2046. public final boolean equals(Object o) {
  2047. return((this == o) ||
  2048. ((o != null) && (getClass() == o.getClass()) &&
  2049. (getPageNumber() == ((DataPage)o).getPageNumber())));
  2050. }
  2051. @Override
  2052. public final String toString() {
  2053. List<Entry> entries = getEntries();
  2054. return (isLeaf() ? "Leaf" : "Node") + "DataPage[" + getPageNumber() +
  2055. "] " + getPrevPageNumber() + ", " + getNextPageNumber() + ", (" +
  2056. getChildTailPageNumber() + "), " +
  2057. ((isLeaf() && !entries.isEmpty()) ?
  2058. ("[" + entries.get(0) + ", " +
  2059. entries.get(entries.size() - 1) + "]") :
  2060. entries);
  2061. }
  2062. }
  2063. }