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.

IndexData.java 86KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951
  1. /*
  2. Copyright (c) 2005 Health Market Science, Inc.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package com.healthmarketscience.jackcess.impl;
  14. import java.io.IOException;
  15. import java.nio.ByteBuffer;
  16. import java.nio.ByteOrder;
  17. import java.util.ArrayList;
  18. import java.util.Arrays;
  19. import java.util.Collection;
  20. import java.util.Collections;
  21. import java.util.Comparator;
  22. import java.util.List;
  23. import java.util.Map;
  24. import com.healthmarketscience.jackcess.ConstraintViolationException;
  25. import com.healthmarketscience.jackcess.Index;
  26. import com.healthmarketscience.jackcess.IndexBuilder;
  27. import com.healthmarketscience.jackcess.RuntimeIOException;
  28. import static com.healthmarketscience.jackcess.impl.ByteUtil.ByteStream;
  29. import static com.healthmarketscience.jackcess.impl.IndexCodes.*;
  30. import org.apache.commons.lang3.builder.ToStringBuilder;
  31. import org.apache.commons.logging.Log;
  32. import org.apache.commons.logging.LogFactory;
  33. /**
  34. * Access table index data. This is the actual data which backs a logical
  35. * Index, where one or more logical indexes can be backed by the same index
  36. * data.
  37. *
  38. * @author Tim McCune
  39. */
  40. public class IndexData {
  41. protected static final Log LOG = LogFactory.getLog(Index.class);
  42. /** special entry which is less than any other entry */
  43. public static final Entry FIRST_ENTRY =
  44. createSpecialEntry(RowIdImpl.FIRST_ROW_ID);
  45. /** special entry which is greater than any other entry */
  46. public static final Entry LAST_ENTRY =
  47. createSpecialEntry(RowIdImpl.LAST_ROW_ID);
  48. /** special object which will always be greater than any other value, when
  49. searching for an index entry range in a multi-value index */
  50. public static final Object MAX_VALUE = new Object();
  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 MIN_VALUE = new Object();
  54. private static final DataPage NEW_ROOT_DATA_PAGE = new RootDataPage();
  55. protected static final int INVALID_INDEX_PAGE_NUMBER = 0;
  56. /** Max number of columns in an index */
  57. public static final int MAX_COLUMNS = 10;
  58. protected static final byte[] EMPTY_PREFIX = new byte[0];
  59. private static final byte[] ASC_EXT_DATE_TRAILER =
  60. {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02};
  61. private static final byte[] DESC_EXT_DATE_TRAILER =
  62. flipBytes(ByteUtil.copyOf(ASC_EXT_DATE_TRAILER, ASC_EXT_DATE_TRAILER.length));
  63. static final short COLUMN_UNUSED = -1;
  64. public static final byte ASCENDING_COLUMN_FLAG = (byte)0x01;
  65. public static final byte UNIQUE_INDEX_FLAG = (byte)0x01;
  66. public static final byte IGNORE_NULLS_INDEX_FLAG = (byte)0x02;
  67. public static final byte REQUIRED_INDEX_FLAG = (byte)0x08;
  68. public static final byte UNKNOWN_INDEX_FLAG = (byte)0x80; // always seems to be set on indexes in access 2000+
  69. private static final int MAGIC_INDEX_NUMBER = 1923;
  70. private static final ByteOrder ENTRY_BYTE_ORDER = ByteOrder.BIG_ENDIAN;
  71. /** type attributes for Entries which simplify comparisons */
  72. public enum EntryType {
  73. /** comparable type indicating this Entry should always compare less than
  74. valid RowIds */
  75. ALWAYS_FIRST,
  76. /** comparable type indicating this Entry should always compare less than
  77. other valid entries with equal entryBytes */
  78. FIRST_VALID,
  79. /** comparable type indicating this RowId should always compare
  80. normally */
  81. NORMAL,
  82. /** comparable type indicating this Entry should always compare greater
  83. than other valid entries with equal entryBytes */
  84. LAST_VALID,
  85. /** comparable type indicating this Entry should always compare greater
  86. than valid RowIds */
  87. ALWAYS_LAST;
  88. }
  89. public static final Comparator<byte[]> BYTE_CODE_COMPARATOR =
  90. new Comparator<byte[]>() {
  91. @Override
  92. public int compare(byte[] left, byte[] right) {
  93. if(left == right) {
  94. return 0;
  95. }
  96. if(left == null) {
  97. return -1;
  98. }
  99. if(right == null) {
  100. return 1;
  101. }
  102. int len = Math.min(left.length, right.length);
  103. int pos = 0;
  104. while((pos < len) && (left[pos] == right[pos])) {
  105. ++pos;
  106. }
  107. if(pos < len) {
  108. return ((ByteUtil.asUnsignedByte(left[pos]) <
  109. ByteUtil.asUnsignedByte(right[pos])) ? -1 : 1);
  110. }
  111. return ((left.length < right.length) ? -1 :
  112. ((left.length > right.length) ? 1 : 0));
  113. }
  114. };
  115. /** name, generated on demand */
  116. private String _name;
  117. /** owning table */
  118. private final TableImpl _table;
  119. /** 0-based index data number */
  120. private final int _number;
  121. /** Page number of the root index data */
  122. private int _rootPageNumber;
  123. /** offset within the tableDefinition buffer of the uniqueEntryCount for
  124. this index */
  125. private final int _uniqueEntryCountOffset;
  126. /** The number of unique entries which have been added to this index. note,
  127. however, that it is never decremented, only incremented (as observed in
  128. Access). */
  129. private int _uniqueEntryCount;
  130. /** List of columns and flags */
  131. private final List<ColumnDescriptor> _columns =
  132. new ArrayList<ColumnDescriptor>();
  133. /** the logical indexes which this index data backs */
  134. private final List<Index> _indexes = new ArrayList<Index>();
  135. /** flags for this index */
  136. private byte _indexFlags;
  137. /** Usage map of pages that this index owns */
  138. private UsageMap _ownedPages;
  139. /** <code>true</code> if the index entries have been initialized,
  140. <code>false</code> otherwise */
  141. private boolean _initialized;
  142. /** modification count for the table, keeps cursors up-to-date */
  143. private int _modCount;
  144. /** temp buffer used to read/write the index pages */
  145. private final TempBufferHolder _indexBufferH =
  146. TempBufferHolder.newHolder(TempBufferHolder.Type.SOFT, true);
  147. /** temp buffer used to create index entries */
  148. private ByteStream _entryBuffer;
  149. /** max size for all the entries written to a given index data page */
  150. private final int _maxPageEntrySize;
  151. /** whether or not this index data is backing a primary key logical index */
  152. private boolean _primaryKey;
  153. /** if non-null, the reason why we cannot create entries for this index */
  154. private String _unsupportedReason;
  155. /** Cache which manages the index pages */
  156. private final IndexPageCache _pageCache;
  157. protected IndexData(TableImpl table, int number, int uniqueEntryCount,
  158. int uniqueEntryCountOffset)
  159. {
  160. _table = table;
  161. _number = number;
  162. _uniqueEntryCount = uniqueEntryCount;
  163. _uniqueEntryCountOffset = uniqueEntryCountOffset;
  164. _maxPageEntrySize = calcMaxPageEntrySize(_table.getFormat());
  165. _pageCache = new IndexPageCache(this);
  166. }
  167. /**
  168. * Creates an IndexData appropriate for the given table, using information
  169. * from the given table definition buffer.
  170. */
  171. public static IndexData create(TableImpl table, ByteBuffer tableBuffer,
  172. int number, JetFormat format)
  173. throws IOException
  174. {
  175. int uniqueEntryCountOffset =
  176. (format.OFFSET_INDEX_DEF_BLOCK +
  177. (number * format.SIZE_INDEX_DEFINITION) + 4);
  178. int uniqueEntryCount = tableBuffer.getInt(uniqueEntryCountOffset);
  179. return new IndexData(table, number, uniqueEntryCount, uniqueEntryCountOffset);
  180. }
  181. public String getName() {
  182. if(_name == null) {
  183. if(_indexes.size() == 1) {
  184. _name = _indexes.get(0).getName();
  185. } else if(!_indexes.isEmpty()) {
  186. List<String> names = new ArrayList<String>(_indexes.size());
  187. for(Index idx : _indexes) {
  188. names.add(idx.getName());
  189. }
  190. _name = names.toString();
  191. } else {
  192. _name = String.valueOf(_number);
  193. }
  194. }
  195. return _name;
  196. }
  197. public TableImpl getTable() {
  198. return _table;
  199. }
  200. public JetFormat getFormat() {
  201. return getTable().getFormat();
  202. }
  203. public PageChannel getPageChannel() {
  204. return getTable().getPageChannel();
  205. }
  206. /**
  207. * @return the "main" logical index which is backed by this data.
  208. */
  209. public Index getPrimaryIndex() {
  210. return _indexes.get(0);
  211. }
  212. /**
  213. * @return All of the Indexes backed by this data (unmodifiable List)
  214. */
  215. public List<Index> getIndexes() {
  216. return Collections.unmodifiableList(_indexes);
  217. }
  218. /**
  219. * Adds a logical index which this data is backing.
  220. */
  221. void addIndex(Index index) {
  222. // we keep foreign key indexes at the back of the list. this way the
  223. // primary index will be a non-foreign key index (if any)
  224. if(index.isForeignKey()) {
  225. _indexes.add(index);
  226. } else {
  227. int pos = _indexes.size();
  228. while(pos > 0) {
  229. if(!_indexes.get(pos - 1).isForeignKey()) {
  230. break;
  231. }
  232. --pos;
  233. }
  234. _indexes.add(pos, index);
  235. // also, keep track of whether or not this is a primary key index
  236. _primaryKey |= index.isPrimaryKey();
  237. }
  238. // force name to be regenerated
  239. _name = null;
  240. }
  241. public byte getIndexFlags() {
  242. return _indexFlags;
  243. }
  244. public int getIndexDataNumber() {
  245. return _number;
  246. }
  247. public int getUniqueEntryCount() {
  248. return _uniqueEntryCount;
  249. }
  250. public int getUniqueEntryCountOffset() {
  251. return _uniqueEntryCountOffset;
  252. }
  253. protected boolean isBackingPrimaryKey() {
  254. return _primaryKey;
  255. }
  256. /**
  257. * Whether or not {@code null} values are actually recorded in the index.
  258. */
  259. public boolean shouldIgnoreNulls() {
  260. return((_indexFlags & IGNORE_NULLS_INDEX_FLAG) != 0);
  261. }
  262. /**
  263. * Whether or not index entries must be unique.
  264. * <p>
  265. * Some notes about uniqueness:
  266. * <ul>
  267. * <li>Access does not seem to consider multiple {@code null} entries
  268. * invalid for a unique index</li>
  269. * <li>text indexes collapse case, and Access seems to compare <b>only</b>
  270. * the index entry bytes, therefore two strings which differ only in
  271. * case <i>will violate</i> the unique constraint</li>
  272. * </ul>
  273. */
  274. public boolean isUnique() {
  275. return(isBackingPrimaryKey() || ((_indexFlags & UNIQUE_INDEX_FLAG) != 0));
  276. }
  277. /**
  278. * Whether or not values are required in the columns.
  279. */
  280. public boolean isRequired() {
  281. return((_indexFlags & REQUIRED_INDEX_FLAG) != 0);
  282. }
  283. /**
  284. * Returns the Columns for this index (unmodifiable)
  285. */
  286. public List<ColumnDescriptor> getColumns() {
  287. return Collections.unmodifiableList(_columns);
  288. }
  289. public int getColumnCount() {
  290. return _columns.size();
  291. }
  292. /**
  293. * Whether or not the complete index state has been read.
  294. */
  295. public boolean isInitialized() {
  296. return _initialized;
  297. }
  298. protected int getRootPageNumber() {
  299. return _rootPageNumber;
  300. }
  301. private void setUnsupportedReason(String reason, ColumnImpl col) {
  302. _unsupportedReason = withErrorContext(reason);
  303. if(!col.getTable().isSystem()) {
  304. LOG.warn(_unsupportedReason + ", making read-only");
  305. } else {
  306. if(LOG.isDebugEnabled()) {
  307. LOG.debug(_unsupportedReason + ", making read-only");
  308. }
  309. }
  310. }
  311. String getUnsupportedReason() {
  312. return _unsupportedReason;
  313. }
  314. protected int getMaxPageEntrySize() {
  315. return _maxPageEntrySize;
  316. }
  317. /**
  318. * Returns the number of database pages owned by this index data.
  319. * @usage _intermediate_method_
  320. */
  321. public int getOwnedPageCount() {
  322. return _ownedPages.getPageCount();
  323. }
  324. void addOwnedPage(int pageNumber) throws IOException {
  325. _ownedPages.addPageNumber(pageNumber);
  326. }
  327. void collectUsageMapPages(Collection<Integer> pages) {
  328. pages.add(_ownedPages.getTablePageNumber());
  329. }
  330. /**
  331. * Used by unit tests to validate the internal status of the index.
  332. * @param forceLoad if {@code false} only validate currently loaded index
  333. * data pages, otherwise, load and validate all index pages
  334. * @usage _advanced_method_
  335. */
  336. public void validate(boolean forceLoad) throws IOException {
  337. _pageCache.validate(forceLoad);
  338. }
  339. /**
  340. * Returns the number of index entries in the index. Only called by unit
  341. * tests.
  342. * <p>
  343. * Forces index initialization.
  344. * @usage _advanced_method_
  345. */
  346. public int getEntryCount()
  347. throws IOException
  348. {
  349. initialize();
  350. EntryCursor cursor = cursor();
  351. Entry endEntry = cursor.getLastEntry();
  352. int count = 0;
  353. while(!endEntry.equals(cursor.getNextEntry())) {
  354. ++count;
  355. }
  356. return count;
  357. }
  358. /**
  359. * Forces initialization of this index (actual parsing of index pages).
  360. * normally, the index will not be initialized until the entries are
  361. * actually needed.
  362. */
  363. public void initialize() throws IOException {
  364. if(!_initialized) {
  365. _pageCache.setRootPageNumber(getRootPageNumber());
  366. _initialized = true;
  367. }
  368. }
  369. /**
  370. * Writes the current index state to the database.
  371. * <p>
  372. * Forces index initialization.
  373. */
  374. public void update() throws IOException
  375. {
  376. // make sure we've parsed the entries
  377. initialize();
  378. if(_unsupportedReason != null) {
  379. throw new UnsupportedOperationException(
  380. "Cannot write indexes of this type due to " + _unsupportedReason);
  381. }
  382. _pageCache.write();
  383. }
  384. /**
  385. * Read the rest of the index info from a tableBuffer
  386. * @param tableBuffer table definition buffer to read from initial info
  387. * @param availableColumns Columns that this index may use
  388. */
  389. public void read(ByteBuffer tableBuffer, List<ColumnImpl> availableColumns)
  390. throws IOException
  391. {
  392. ByteUtil.forward(tableBuffer, getFormat().SKIP_BEFORE_INDEX); //Forward past Unknown
  393. for (int i = 0; i < MAX_COLUMNS; i++) {
  394. short columnNumber = tableBuffer.getShort();
  395. byte colFlags = tableBuffer.get();
  396. if (columnNumber != COLUMN_UNUSED) {
  397. // find the desired column by column number (which is not necessarily
  398. // the same as the column index)
  399. ColumnImpl idxCol = null;
  400. for(ColumnImpl col : availableColumns) {
  401. if(col.getColumnNumber() == columnNumber) {
  402. idxCol = col;
  403. break;
  404. }
  405. }
  406. if(idxCol == null) {
  407. throw new IOException(withErrorContext(
  408. "Could not find column with number "
  409. + columnNumber + " for index"));
  410. }
  411. _columns.add(newColumnDescriptor(idxCol, colFlags));
  412. }
  413. }
  414. _ownedPages = UsageMap.read(getTable().getDatabase(), tableBuffer);
  415. _rootPageNumber = tableBuffer.getInt();
  416. ByteUtil.forward(tableBuffer, getFormat().SKIP_BEFORE_INDEX_FLAGS); //Forward past Unknown
  417. _indexFlags = tableBuffer.get();
  418. ByteUtil.forward(tableBuffer, getFormat().SKIP_AFTER_INDEX_FLAGS); //Forward past other stuff
  419. }
  420. /**
  421. * Writes the index row count definitions into a table definition buffer.
  422. * @param creator description of the indexes to write
  423. * @param buffer Buffer to write to
  424. */
  425. protected static void writeRowCountDefinitions(
  426. TableCreator creator, ByteBuffer buffer)
  427. {
  428. writeRowCountDefinitions(creator, buffer, creator.getIndexCount());
  429. }
  430. /**
  431. * Writes the index row count definitions into a table definition buffer.
  432. * @param creator description of the indexes to write
  433. * @param buffer Buffer to write to
  434. * @param idxCount num indexes to write
  435. */
  436. protected static void writeRowCountDefinitions(
  437. TableMutator creator, ByteBuffer buffer, int idxCount)
  438. {
  439. // index row counts (empty data)
  440. ByteUtil.forward(buffer, (idxCount *
  441. creator.getFormat().SIZE_INDEX_DEFINITION));
  442. }
  443. /**
  444. * Writes the index definitions into a table definition buffer.
  445. * @param creator description of the indexes to write
  446. * @param buffer Buffer to write to
  447. */
  448. protected static void writeDefinitions(
  449. TableCreator creator, ByteBuffer buffer)
  450. throws IOException
  451. {
  452. ByteBuffer rootPageBuffer = createRootPageBuffer(creator);
  453. for(TableMutator.IndexDataState idxDataState : creator.getIndexDataStates()) {
  454. writeDefinition(creator, buffer, idxDataState, rootPageBuffer);
  455. }
  456. }
  457. /**
  458. * Writes the index definitions into a table definition buffer.
  459. * @param creator description of the indexes to write
  460. * @param buffer Buffer to write to
  461. */
  462. @SuppressWarnings("resource")
  463. protected static void writeDefinition(
  464. TableMutator creator, ByteBuffer buffer,
  465. TableMutator.IndexDataState idxDataState, ByteBuffer rootPageBuffer)
  466. throws IOException
  467. {
  468. if(rootPageBuffer == null) {
  469. rootPageBuffer = createRootPageBuffer(creator);
  470. }
  471. buffer.putInt(MAGIC_INDEX_NUMBER); // seemingly constant magic value
  472. // write column information (always MAX_COLUMNS entries)
  473. IndexBuilder idx = idxDataState.getFirstIndex();
  474. List<IndexBuilder.Column> idxColumns = idx.getColumns();
  475. for(int i = 0; i < MAX_COLUMNS; ++i) {
  476. short columnNumber = COLUMN_UNUSED;
  477. byte flags = 0;
  478. if(i < idxColumns.size()) {
  479. // determine column info
  480. IndexBuilder.Column idxCol = idxColumns.get(i);
  481. flags = idxCol.getFlags();
  482. // find actual table column number
  483. columnNumber = creator.getColumnNumber(idxCol.getName());
  484. if(columnNumber == COLUMN_UNUSED) {
  485. // should never happen as this is validated before
  486. throw new IllegalArgumentException(
  487. withErrorContext(
  488. "Column with name " + idxCol.getName() + " not found",
  489. creator.getDatabase(), creator.getTableName(), idx.getName()));
  490. }
  491. }
  492. buffer.putShort(columnNumber); // table column number
  493. buffer.put(flags); // column flags (e.g. ordering)
  494. }
  495. buffer.put(idxDataState.getUmapRowNumber()); // umap row
  496. ByteUtil.put3ByteInt(buffer, idxDataState.getUmapPageNumber()); // umap page
  497. // write empty root index page
  498. creator.getPageChannel().writePage(rootPageBuffer,
  499. idxDataState.getRootPageNumber());
  500. buffer.putInt(idxDataState.getRootPageNumber());
  501. buffer.putInt(0); // unknown
  502. buffer.put(idx.getFlags()); // index flags (unique, etc.)
  503. ByteUtil.forward(buffer, 5); // unknown
  504. }
  505. private static ByteBuffer createRootPageBuffer(TableMutator creator)
  506. throws IOException
  507. {
  508. ByteBuffer rootPageBuffer = creator.getPageChannel().createPageBuffer();
  509. writeDataPage(rootPageBuffer, NEW_ROOT_DATA_PAGE,
  510. creator.getTdefPageNumber(), creator.getFormat());
  511. return rootPageBuffer;
  512. }
  513. /**
  514. * Prepares to add a row to this index. All constraints are checked before
  515. * this method returns.
  516. * <p>
  517. * Forces index initialization.
  518. *
  519. * @param row Row to add
  520. * @param rowId rowId of the row to be added
  521. *
  522. * @return a PendingChange which can complete the addition or roll it back
  523. */
  524. public PendingChange prepareAddRow(Object[] row, RowIdImpl rowId,
  525. PendingChange nextChange)
  526. throws IOException
  527. {
  528. return prepareAddRow(row, rowId, new AddRowPendingChange(nextChange));
  529. }
  530. private PendingChange prepareAddRow(Object[] row, RowIdImpl rowId,
  531. AddRowPendingChange change)
  532. throws IOException
  533. {
  534. int nullCount = countNullValues(row);
  535. boolean isNullEntry = (nullCount == _columns.size());
  536. if(shouldIgnoreNulls() && isNullEntry) {
  537. // nothing to do
  538. return change;
  539. }
  540. if((nullCount > 0) && (isBackingPrimaryKey() || isRequired())) {
  541. throw new ConstraintViolationException(withErrorContext(
  542. "Null value found in row " + Arrays.asList(row) +
  543. " for primary key or required index"));
  544. }
  545. // make sure we've parsed the entries
  546. initialize();
  547. return prepareAddEntry(new Entry(createEntryBytes(row), rowId), isNullEntry,
  548. row, change);
  549. }
  550. /**
  551. * Adds an entry to the correct index dataPage, maintaining the order.
  552. */
  553. private PendingChange prepareAddEntry(Entry newEntry, boolean isNullEntry,
  554. Object[] row, AddRowPendingChange change)
  555. throws IOException
  556. {
  557. DataPage dataPage = findDataPage(newEntry);
  558. int idx = dataPage.findEntry(newEntry);
  559. if(idx < 0) {
  560. // this is a new entry
  561. idx = missingIndexToInsertionPoint(idx);
  562. Position newPos = new Position(dataPage, idx, newEntry, true);
  563. Position nextPos = getNextPosition(newPos);
  564. Position prevPos = getPreviousPosition(newPos);
  565. // determine if the addition of this entry would break the uniqueness
  566. // constraint. See isUnique() for some notes about uniqueness as
  567. // defined by Access.
  568. boolean isDupeEntry =
  569. (((nextPos != null) &&
  570. newEntry.equalsEntryBytes(nextPos.getEntry())) ||
  571. ((prevPos != null) &&
  572. newEntry.equalsEntryBytes(prevPos.getEntry())));
  573. if(isUnique() && !isNullEntry && isDupeEntry) {
  574. throw new ConstraintViolationException(withErrorContext(
  575. "New row " + Arrays.asList(row) +
  576. " violates uniqueness constraint for index"));
  577. }
  578. change.setAddRow(newEntry, dataPage, idx, isDupeEntry);
  579. } else {
  580. change.setOldRow(newEntry);
  581. }
  582. return change;
  583. }
  584. /**
  585. * Completes a prepared row addition.
  586. */
  587. private void commitAddRow(Entry newEntry, DataPage dataPage, int idx,
  588. boolean isDupeEntry, Entry oldEntry)
  589. throws IOException
  590. {
  591. if(newEntry != null) {
  592. dataPage.addEntry(idx, newEntry);
  593. // if we are adding a duplicate entry, or replacing an existing entry,
  594. // then the unique entry count doesn't change
  595. if(!isDupeEntry && (oldEntry == null)) {
  596. ++_uniqueEntryCount;
  597. }
  598. ++_modCount;
  599. } else {
  600. LOG.warn(withErrorContext("Added duplicate index entry " + oldEntry));
  601. }
  602. }
  603. /**
  604. * Prepares to update a row in this index. All constraints are checked
  605. * before this method returns.
  606. * <p>
  607. * Forces index initialization.
  608. *
  609. * @param oldRow Row to be removed
  610. * @param newRow Row to be added
  611. * @param rowId rowId of the row to be updated
  612. *
  613. * @return a PendingChange which can complete the update or roll it back
  614. */
  615. public PendingChange prepareUpdateRow(Object[] oldRow, RowIdImpl rowId,
  616. Object[] newRow,
  617. PendingChange nextChange)
  618. throws IOException
  619. {
  620. UpdateRowPendingChange change = new UpdateRowPendingChange(nextChange);
  621. change.setOldRow(deleteRowImpl(oldRow, rowId));
  622. try {
  623. prepareAddRow(newRow, rowId, change);
  624. return change;
  625. } catch(ConstraintViolationException e) {
  626. // need to undo the deletion before bailing
  627. change.rollback();
  628. throw e;
  629. }
  630. }
  631. /**
  632. * Removes a row from this index
  633. * <p>
  634. * Forces index initialization.
  635. *
  636. * @param row Row to remove
  637. * @param rowId rowId of the row to be removed
  638. */
  639. public void deleteRow(Object[] row, RowIdImpl rowId)
  640. throws IOException
  641. {
  642. deleteRowImpl(row, rowId);
  643. }
  644. private Entry deleteRowImpl(Object[] row, RowIdImpl rowId)
  645. throws IOException
  646. {
  647. int nullCount = countNullValues(row);
  648. if(shouldIgnoreNulls() && (nullCount == _columns.size())) {
  649. // nothing to do
  650. return null;
  651. }
  652. // make sure we've parsed the entries
  653. initialize();
  654. Entry oldEntry = new Entry(createEntryBytes(row), rowId);
  655. Entry removedEntry = removeEntry(oldEntry);
  656. if(removedEntry != null) {
  657. ++_modCount;
  658. } else {
  659. LOG.warn(withErrorContext(
  660. "Failed removing index entry " + oldEntry + " for row: " +
  661. Arrays.asList(row)));
  662. }
  663. return removedEntry;
  664. }
  665. /**
  666. * Undoes a previous row deletion.
  667. */
  668. private void rollbackDeletedRow(Entry removedEntry)
  669. throws IOException
  670. {
  671. if(removedEntry == null) {
  672. // no change was made
  673. return;
  674. }
  675. // unfortunately, stuff might have shuffled around when we first removed
  676. // the row, so in order to re-insert it, we need to re-find and insert it.
  677. DataPage dataPage = findDataPage(removedEntry);
  678. int idx = dataPage.findEntry(removedEntry);
  679. if(idx < 0) {
  680. dataPage.addEntry(missingIndexToInsertionPoint(idx), removedEntry);
  681. }
  682. }
  683. /**
  684. * Removes an entry from the relevant index dataPage, maintaining the order.
  685. * Will search by RowId if entry is not found (in case a partial entry was
  686. * provided).
  687. */
  688. private Entry removeEntry(Entry oldEntry)
  689. throws IOException
  690. {
  691. DataPage dataPage = findDataPage(oldEntry);
  692. int idx = dataPage.findEntry(oldEntry);
  693. boolean doRemove = false;
  694. if(idx < 0) {
  695. // the caller may have only read some of the row data, if this is the
  696. // case, just search for the page/row numbers
  697. // TODO, we could force caller to get relevant values?
  698. EntryCursor cursor = cursor();
  699. Position tmpPos = null;
  700. Position endPos = cursor._lastPos;
  701. while(!endPos.equals(
  702. tmpPos = cursor.getAnotherPosition(CursorImpl.MOVE_FORWARD))) {
  703. if(tmpPos.getEntry().getRowId().equals(oldEntry.getRowId())) {
  704. dataPage = tmpPos.getDataPage();
  705. idx = tmpPos.getIndex();
  706. doRemove = true;
  707. break;
  708. }
  709. }
  710. } else {
  711. doRemove = true;
  712. }
  713. Entry removedEntry = null;
  714. if(doRemove) {
  715. // found it!
  716. removedEntry = dataPage.removeEntry(idx);
  717. }
  718. return removedEntry;
  719. }
  720. public static void commitAll(PendingChange change) throws IOException {
  721. while(change != null) {
  722. change.commit();
  723. change = change.getNext();
  724. }
  725. }
  726. public static void rollbackAll(PendingChange change) throws IOException {
  727. while(change != null) {
  728. change.rollback();
  729. change = change.getNext();
  730. }
  731. }
  732. /**
  733. * Gets a new cursor for this index.
  734. * <p>
  735. * Forces index initialization.
  736. */
  737. public EntryCursor cursor()
  738. throws IOException
  739. {
  740. return cursor(null, true, null, true);
  741. }
  742. /**
  743. * Gets a new cursor for this index, narrowed to the range defined by the
  744. * given startRow and endRow.
  745. * <p>
  746. * Forces index initialization.
  747. *
  748. * @param startRow the first row of data for the cursor, or {@code null} for
  749. * the first entry
  750. * @param startInclusive whether or not startRow is inclusive or exclusive
  751. * @param endRow the last row of data for the cursor, or {@code null} for
  752. * the last entry
  753. * @param endInclusive whether or not endRow is inclusive or exclusive
  754. */
  755. public EntryCursor cursor(Object[] startRow,
  756. boolean startInclusive,
  757. Object[] endRow,
  758. boolean endInclusive)
  759. throws IOException
  760. {
  761. initialize();
  762. Entry startEntry = FIRST_ENTRY;
  763. byte[] startEntryBytes = null;
  764. if(startRow != null) {
  765. startEntryBytes = createEntryBytes(startRow);
  766. startEntry = new Entry(startEntryBytes,
  767. (startInclusive ?
  768. RowIdImpl.FIRST_ROW_ID : RowIdImpl.LAST_ROW_ID));
  769. }
  770. Entry endEntry = LAST_ENTRY;
  771. if(endRow != null) {
  772. // reuse startEntryBytes if startRow and endRow are same array. this is
  773. // common for "lookup" code
  774. byte[] endEntryBytes = ((startRow == endRow) ?
  775. startEntryBytes :
  776. createEntryBytes(endRow));
  777. endEntry = new Entry(endEntryBytes,
  778. (endInclusive ?
  779. RowIdImpl.LAST_ROW_ID : RowIdImpl.FIRST_ROW_ID));
  780. }
  781. return new EntryCursor(findEntryPosition(startEntry),
  782. findEntryPosition(endEntry));
  783. }
  784. private Position findEntryPosition(Entry entry)
  785. throws IOException
  786. {
  787. DataPage dataPage = findDataPage(entry);
  788. int idx = dataPage.findEntry(entry);
  789. boolean between = false;
  790. if(idx < 0) {
  791. // given entry was not found exactly. our current position is now
  792. // really between two indexes, but we cannot support that as an integer
  793. // value, so we set a flag instead
  794. idx = missingIndexToInsertionPoint(idx);
  795. between = true;
  796. }
  797. return new Position(dataPage, idx, entry, between);
  798. }
  799. private Position getNextPosition(Position curPos)
  800. throws IOException
  801. {
  802. // get the next index (between-ness is handled internally)
  803. int nextIdx = curPos.getNextIndex();
  804. Position nextPos = null;
  805. if(nextIdx < curPos.getDataPage().getEntries().size()) {
  806. nextPos = new Position(curPos.getDataPage(), nextIdx);
  807. } else {
  808. int nextPageNumber = curPos.getDataPage().getNextPageNumber();
  809. DataPage nextDataPage = null;
  810. while(nextPageNumber != INVALID_INDEX_PAGE_NUMBER) {
  811. DataPage dp = getDataPage(nextPageNumber);
  812. if(!dp.isEmpty()) {
  813. nextDataPage = dp;
  814. break;
  815. }
  816. nextPageNumber = dp.getNextPageNumber();
  817. }
  818. if(nextDataPage != null) {
  819. nextPos = new Position(nextDataPage, 0);
  820. }
  821. }
  822. return nextPos;
  823. }
  824. /**
  825. * Returns the Position before the given one, or {@code null} if none.
  826. */
  827. private Position getPreviousPosition(Position curPos)
  828. throws IOException
  829. {
  830. // get the previous index (between-ness is handled internally)
  831. int prevIdx = curPos.getPrevIndex();
  832. Position prevPos = null;
  833. if(prevIdx >= 0) {
  834. prevPos = new Position(curPos.getDataPage(), prevIdx);
  835. } else {
  836. int prevPageNumber = curPos.getDataPage().getPrevPageNumber();
  837. DataPage prevDataPage = null;
  838. while(prevPageNumber != INVALID_INDEX_PAGE_NUMBER) {
  839. DataPage dp = getDataPage(prevPageNumber);
  840. if(!dp.isEmpty()) {
  841. prevDataPage = dp;
  842. break;
  843. }
  844. prevPageNumber = dp.getPrevPageNumber();
  845. }
  846. if(prevDataPage != null) {
  847. prevPos = new Position(prevDataPage,
  848. (prevDataPage.getEntries().size() - 1));
  849. }
  850. }
  851. return prevPos;
  852. }
  853. /**
  854. * Returns the valid insertion point for an index indicating a missing
  855. * entry.
  856. */
  857. protected static int missingIndexToInsertionPoint(int idx) {
  858. return -(idx + 1);
  859. }
  860. /**
  861. * Constructs an array of values appropriate for this index from the given
  862. * column values, expected to match the columns for this index.
  863. * @return the appropriate sparse array of data
  864. * @throws IllegalArgumentException if the wrong number of values are
  865. * provided
  866. */
  867. public Object[] constructIndexRowFromEntry(Object... values)
  868. {
  869. if(values.length != _columns.size()) {
  870. throw new IllegalArgumentException(withErrorContext(
  871. "Wrong number of column values given " + values.length +
  872. ", expected " + _columns.size()));
  873. }
  874. int valIdx = 0;
  875. Object[] idxRow = new Object[getTable().getColumnCount()];
  876. for(ColumnDescriptor col : _columns) {
  877. idxRow[col.getColumnIndex()] = values[valIdx++];
  878. }
  879. return idxRow;
  880. }
  881. /**
  882. * Constructs an array of values appropriate for this index from the given
  883. * column values, possibly only providing a prefix subset of the index
  884. * columns (at least one value must be provided). If a prefix entry is
  885. * provided, any missing, trailing index entry values will use the given
  886. * filler value.
  887. * @return the appropriate sparse array of data
  888. * @throws IllegalArgumentException if at least one value is not provided
  889. */
  890. public Object[] constructPartialIndexRowFromEntry(
  891. Object filler, Object... values)
  892. {
  893. if(values.length == 0) {
  894. throw new IllegalArgumentException(withErrorContext(
  895. "At least one column value must be provided"));
  896. }
  897. if(values.length > _columns.size()) {
  898. throw new IllegalArgumentException(withErrorContext(
  899. "Too many column values given " + values.length +
  900. ", expected at most " + _columns.size()));
  901. }
  902. int valIdx = 0;
  903. Object[] idxRow = new Object[getTable().getColumnCount()];
  904. for(ColumnDescriptor col : _columns) {
  905. idxRow[col.getColumnIndex()] =
  906. ((valIdx < values.length) ? values[valIdx] : filler);
  907. ++valIdx;
  908. }
  909. return idxRow;
  910. }
  911. /**
  912. * Constructs an array of values appropriate for this index from the given
  913. * column value.
  914. * @return the appropriate sparse array of data or {@code null} if not all
  915. * columns for this index were provided
  916. */
  917. public Object[] constructIndexRow(String colName, Object value)
  918. {
  919. return constructIndexRow(Collections.singletonMap(colName, value));
  920. }
  921. /**
  922. * Constructs an array of values appropriate for this index from the given
  923. * column value, which must be the first column of the index. Any missing,
  924. * trailing index entry values will use the given filler value.
  925. * @return the appropriate sparse array of data or {@code null} if no prefix
  926. * list of columns for this index were provided
  927. */
  928. public Object[] constructPartialIndexRow(Object filler, String colName, Object value)
  929. {
  930. return constructPartialIndexRow(filler, Collections.singletonMap(colName, value));
  931. }
  932. /**
  933. * Constructs an array of values appropriate for this index from the given
  934. * column values.
  935. * @return the appropriate sparse array of data or {@code null} if not all
  936. * columns for this index were provided
  937. */
  938. public Object[] constructIndexRow(Map<String,?> row)
  939. {
  940. for(ColumnDescriptor col : _columns) {
  941. if(!row.containsKey(col.getName())) {
  942. return null;
  943. }
  944. }
  945. Object[] idxRow = new Object[getTable().getColumnCount()];
  946. for(ColumnDescriptor col : _columns) {
  947. idxRow[col.getColumnIndex()] = row.get(col.getName());
  948. }
  949. return idxRow;
  950. }
  951. /**
  952. * Constructs an array of values appropriate for this index from the given
  953. * column values, possibly only using a subset of the given values. A
  954. * partial row can be created if one or more prefix column values are
  955. * provided. If a prefix can be found, any missing, trailing index entry
  956. * values will use the given filler value.
  957. * @return the appropriate sparse array of data or {@code null} if no prefix
  958. * list of columns for this index were provided
  959. */
  960. public Object[] constructPartialIndexRow(Object filler, Map<String,?> row)
  961. {
  962. // see if we have at least one prefix column
  963. int numCols = 0;
  964. for(ColumnDescriptor col : _columns) {
  965. if(!row.containsKey(col.getName())) {
  966. if(numCols == 0) {
  967. // can't do it, need at least first column
  968. return null;
  969. }
  970. break;
  971. }
  972. ++numCols;
  973. }
  974. // fill in the row with either the prefix values or the filler value, as
  975. // appropriate
  976. Object[] idxRow = new Object[getTable().getColumnCount()];
  977. int valIdx = 0;
  978. for(ColumnDescriptor col : _columns) {
  979. idxRow[col.getColumnIndex()] =
  980. ((valIdx < numCols) ? row.get(col.getName()) : filler);
  981. ++valIdx;
  982. }
  983. return idxRow;
  984. }
  985. @Override
  986. public String toString() {
  987. ToStringBuilder sb = CustomToStringStyle.builder(this)
  988. .append("dataNumber", _number)
  989. .append("pageNumber", _rootPageNumber)
  990. .append("isBackingPrimaryKey", isBackingPrimaryKey())
  991. .append("isUnique", isUnique())
  992. .append("ignoreNulls", shouldIgnoreNulls())
  993. .append("isRequired", isRequired())
  994. .append("columns", _columns)
  995. .append("initialized", _initialized);
  996. if(_initialized) {
  997. try {
  998. sb.append("entryCount", getEntryCount());
  999. } catch(IOException e) {
  1000. throw new RuntimeIOException(e);
  1001. }
  1002. }
  1003. sb.append("pageCache", _pageCache);
  1004. return sb.toString();
  1005. }
  1006. /**
  1007. * Write the given index page out to a buffer
  1008. */
  1009. protected void writeDataPage(DataPage dataPage)
  1010. throws IOException
  1011. {
  1012. if(dataPage.getCompressedEntrySize() > _maxPageEntrySize) {
  1013. throw new IllegalStateException(withErrorContext("data page is too large"));
  1014. }
  1015. ByteBuffer buffer = _indexBufferH.getPageBuffer(getPageChannel());
  1016. writeDataPage(buffer, dataPage, getTable().getTableDefPageNumber(),
  1017. getFormat());
  1018. getPageChannel().writePage(buffer, dataPage.getPageNumber());
  1019. }
  1020. /**
  1021. * Writes the data page info to the given buffer.
  1022. */
  1023. protected static void writeDataPage(ByteBuffer buffer, DataPage dataPage,
  1024. int tdefPageNumber, JetFormat format)
  1025. throws IOException
  1026. {
  1027. buffer.put(dataPage.isLeaf() ?
  1028. PageTypes.INDEX_LEAF :
  1029. PageTypes.INDEX_NODE ); //Page type
  1030. buffer.put((byte) 0x01); //Unknown
  1031. buffer.putShort((short) 0); //Free space
  1032. buffer.putInt(tdefPageNumber);
  1033. buffer.putInt(0); //Unknown
  1034. buffer.putInt(dataPage.getPrevPageNumber()); //Prev page
  1035. buffer.putInt(dataPage.getNextPageNumber()); //Next page
  1036. buffer.putInt(dataPage.getChildTailPageNumber()); //ChildTail page
  1037. byte[] entryPrefix = dataPage.getEntryPrefix();
  1038. buffer.putShort((short) entryPrefix.length); // entry prefix byte count
  1039. buffer.put((byte) 0); //Unknown
  1040. byte[] entryMask = new byte[format.SIZE_INDEX_ENTRY_MASK];
  1041. // first entry includes the prefix
  1042. int totalSize = entryPrefix.length;
  1043. for(Entry entry : dataPage.getEntries()) {
  1044. totalSize += (entry.size() - entryPrefix.length);
  1045. int idx = totalSize / 8;
  1046. entryMask[idx] |= (1 << (totalSize % 8));
  1047. }
  1048. buffer.put(entryMask);
  1049. // first entry includes the prefix
  1050. buffer.put(entryPrefix);
  1051. for(Entry entry : dataPage.getEntries()) {
  1052. entry.write(buffer, entryPrefix);
  1053. }
  1054. // update free space
  1055. buffer.putShort(2, (short) (format.PAGE_SIZE - buffer.position()));
  1056. }
  1057. /**
  1058. * Reads an index page, populating the correct collection based on the page
  1059. * type (node or leaf).
  1060. */
  1061. protected void readDataPage(DataPage dataPage)
  1062. throws IOException
  1063. {
  1064. ByteBuffer buffer = _indexBufferH.getPageBuffer(getPageChannel());
  1065. getPageChannel().readPage(buffer, dataPage.getPageNumber());
  1066. boolean isLeaf = isLeafPage(buffer);
  1067. dataPage.setLeaf(isLeaf);
  1068. // note, "header" data is in LITTLE_ENDIAN format, entry data is in
  1069. // BIG_ENDIAN format
  1070. int entryPrefixLength = ByteUtil.getUnsignedShort(
  1071. buffer, getFormat().OFFSET_INDEX_COMPRESSED_BYTE_COUNT);
  1072. int entryMaskLength = getFormat().SIZE_INDEX_ENTRY_MASK;
  1073. int entryMaskPos = getFormat().OFFSET_INDEX_ENTRY_MASK;
  1074. int entryPos = entryMaskPos + entryMaskLength;
  1075. int lastStart = 0;
  1076. int totalEntrySize = 0;
  1077. byte[] entryPrefix = null;
  1078. List<Entry> entries = new ArrayList<Entry>();
  1079. TempBufferHolder tmpEntryBufferH =
  1080. TempBufferHolder.newHolder(TempBufferHolder.Type.HARD, true,
  1081. ENTRY_BYTE_ORDER);
  1082. Entry prevEntry = FIRST_ENTRY;
  1083. for (int i = 0; i < entryMaskLength; i++) {
  1084. byte entryMask = buffer.get(entryMaskPos + i);
  1085. for (int j = 0; j < 8; j++) {
  1086. if ((entryMask & (1 << j)) != 0) {
  1087. int length = (i * 8) + j - lastStart;
  1088. buffer.position(entryPos + lastStart);
  1089. // determine if we can read straight from the index page (if no
  1090. // entryPrefix). otherwise, create temp buf with complete entry.
  1091. ByteBuffer curEntryBuffer = buffer;
  1092. int curEntryLen = length;
  1093. if(entryPrefix != null) {
  1094. curEntryBuffer = getTempEntryBuffer(
  1095. buffer, length, entryPrefix, tmpEntryBufferH);
  1096. curEntryLen += entryPrefix.length;
  1097. }
  1098. totalEntrySize += curEntryLen;
  1099. Entry entry = newEntry(curEntryBuffer, curEntryLen, isLeaf);
  1100. if(prevEntry.compareTo(entry) >= 0) {
  1101. throw new IOException(withErrorContext(
  1102. "Unexpected order in index entries, " +
  1103. prevEntry + " >= " + entry));
  1104. }
  1105. entries.add(entry);
  1106. if((entries.size() == 1) && (entryPrefixLength > 0)) {
  1107. // read any shared entry prefix
  1108. entryPrefix = new byte[entryPrefixLength];
  1109. buffer.position(entryPos + lastStart);
  1110. buffer.get(entryPrefix);
  1111. }
  1112. lastStart += length;
  1113. prevEntry = entry;
  1114. }
  1115. }
  1116. }
  1117. dataPage.setEntryPrefix(entryPrefix != null ? entryPrefix : EMPTY_PREFIX);
  1118. dataPage.setEntries(entries);
  1119. dataPage.setTotalEntrySize(totalEntrySize);
  1120. int prevPageNumber = buffer.getInt(getFormat().OFFSET_PREV_INDEX_PAGE);
  1121. int nextPageNumber = buffer.getInt(getFormat().OFFSET_NEXT_INDEX_PAGE);
  1122. int childTailPageNumber =
  1123. buffer.getInt(getFormat().OFFSET_CHILD_TAIL_INDEX_PAGE);
  1124. dataPage.setPrevPageNumber(prevPageNumber);
  1125. dataPage.setNextPageNumber(nextPageNumber);
  1126. dataPage.setChildTailPageNumber(childTailPageNumber);
  1127. }
  1128. /**
  1129. * Returns a new Entry of the correct type for the given data and page type.
  1130. */
  1131. private static Entry newEntry(ByteBuffer buffer, int entryLength,
  1132. boolean isLeaf)
  1133. throws IOException
  1134. {
  1135. if(isLeaf) {
  1136. return new Entry(buffer, entryLength);
  1137. }
  1138. return new NodeEntry(buffer, entryLength);
  1139. }
  1140. /**
  1141. * Returns an entry buffer containing the relevant data for an entry given
  1142. * the valuePrefix.
  1143. */
  1144. private ByteBuffer getTempEntryBuffer(
  1145. ByteBuffer indexPage, int entryLen, byte[] valuePrefix,
  1146. TempBufferHolder tmpEntryBufferH)
  1147. {
  1148. ByteBuffer tmpEntryBuffer = tmpEntryBufferH.getBuffer(
  1149. getPageChannel(), valuePrefix.length + entryLen);
  1150. // combine valuePrefix and rest of entry from indexPage, then prep for
  1151. // reading
  1152. tmpEntryBuffer.put(valuePrefix);
  1153. tmpEntryBuffer.put(indexPage.array(), indexPage.position(), entryLen);
  1154. tmpEntryBuffer.flip();
  1155. return tmpEntryBuffer;
  1156. }
  1157. /**
  1158. * Determines if the given index page is a leaf or node page.
  1159. */
  1160. private boolean isLeafPage(ByteBuffer buffer)
  1161. throws IOException
  1162. {
  1163. byte pageType = buffer.get(0);
  1164. if(pageType == PageTypes.INDEX_LEAF) {
  1165. return true;
  1166. } else if(pageType == PageTypes.INDEX_NODE) {
  1167. return false;
  1168. }
  1169. throw new IOException(withErrorContext("Unexpected page type " + pageType));
  1170. }
  1171. /**
  1172. * Determines the number of {@code null} values for this index from the
  1173. * given row.
  1174. */
  1175. private int countNullValues(Object[] values)
  1176. {
  1177. if(values == null) {
  1178. return _columns.size();
  1179. }
  1180. // annoyingly, the values array could come from different sources, one
  1181. // of which will make it a different size than the other. we need to
  1182. // handle both situations.
  1183. int nullCount = 0;
  1184. for(ColumnDescriptor col : _columns) {
  1185. Object value = values[col.getColumnIndex()];
  1186. if(col.isNullValue(value)) {
  1187. ++nullCount;
  1188. }
  1189. }
  1190. return nullCount;
  1191. }
  1192. /**
  1193. * Creates the entry bytes for a row of values.
  1194. */
  1195. private byte[] createEntryBytes(Object[] values) throws IOException
  1196. {
  1197. if(values == null) {
  1198. return null;
  1199. }
  1200. if(_entryBuffer == null) {
  1201. _entryBuffer = new ByteStream();
  1202. }
  1203. _entryBuffer.reset();
  1204. for(ColumnDescriptor col : _columns) {
  1205. Object value = values[col.getColumnIndex()];
  1206. if(ColumnImpl.isRawData(value)) {
  1207. // ignore it, we could not parse it
  1208. continue;
  1209. }
  1210. if(value == MIN_VALUE) {
  1211. // null is the "least" value (note the column "ascending" flag is
  1212. // irrelevant here because the entry bytes are _always_ interpreted
  1213. // least to greatest)
  1214. _entryBuffer.write(getNullEntryFlag(true));
  1215. continue;
  1216. }
  1217. if(value == MAX_VALUE) {
  1218. // the opposite null is the "greatest" value (note the column
  1219. // "ascending" flag is irrelevant here because the entry bytes are
  1220. // _always_ interpreted least to greatest)
  1221. _entryBuffer.write(getNullEntryFlag(false));
  1222. continue;
  1223. }
  1224. col.writeValue(value, _entryBuffer);
  1225. }
  1226. return _entryBuffer.toByteArray();
  1227. }
  1228. /**
  1229. * Finds the data page for the given entry.
  1230. */
  1231. protected DataPage findDataPage(Entry entry)
  1232. throws IOException
  1233. {
  1234. return _pageCache.findCacheDataPage(entry);
  1235. }
  1236. /**
  1237. * Gets the data page for the pageNumber.
  1238. */
  1239. protected DataPage getDataPage(int pageNumber)
  1240. throws IOException
  1241. {
  1242. return _pageCache.getCacheDataPage(pageNumber);
  1243. }
  1244. /**
  1245. * Flips the first bit in the byte at the given index.
  1246. */
  1247. private static byte[] flipFirstBitInByte(byte[] value, int index)
  1248. {
  1249. value[index] = (byte)(value[index] ^ 0x80);
  1250. return value;
  1251. }
  1252. /**
  1253. * Flips all the bits in the byte array.
  1254. */
  1255. private static byte[] flipBytes(byte[] value) {
  1256. return flipBytes(value, 0, value.length);
  1257. }
  1258. /**
  1259. * Flips the bits in the specified bytes in the byte array.
  1260. */
  1261. static byte[] flipBytes(byte[] value, int offset, int length) {
  1262. for(int i = offset; i < (offset + length); ++i) {
  1263. value[i] = (byte)(~value[i]);
  1264. }
  1265. return value;
  1266. }
  1267. /**
  1268. * Writes the value of the given column type to a byte array and returns it.
  1269. */
  1270. private static byte[] encodeNumberColumnValue(Object value, ColumnImpl column)
  1271. throws IOException
  1272. {
  1273. // always write in big endian order
  1274. return column.write(value, 0, ENTRY_BYTE_ORDER).array();
  1275. }
  1276. /**
  1277. * Writes a binary value using the general binary entry encoding rules.
  1278. */
  1279. private static void writeGeneralBinaryEntry(byte[] valueBytes, boolean isAsc,
  1280. ByteStream bout)
  1281. {
  1282. int dataLen = valueBytes.length;
  1283. int extraLen = (dataLen + 7) / 8;
  1284. int entryLen = ((dataLen + extraLen + 8) / 9) * 9;
  1285. // reserve space for the full entry
  1286. bout.ensureNewCapacity(entryLen);
  1287. // binary data is written in 8 byte segments with a trailing length byte.
  1288. // The length byte is the amount of valid bytes in the segment (where 9
  1289. // indicates that there is more data _after_ this segment).
  1290. byte[] partialEntryBytes = new byte[9];
  1291. // bit twiddling rules:
  1292. // - isAsc => nothing
  1293. // - !isAsc => flipBytes, _but keep intermediate 09 unflipped_!
  1294. // first, write any intermediate segements
  1295. int segmentLen = dataLen;
  1296. int pos = 0;
  1297. while(segmentLen > 8) {
  1298. System.arraycopy(valueBytes, pos, partialEntryBytes, 0, 8);
  1299. if(!isAsc) {
  1300. // note, we do _not_ flip the length byte for intermediate segments
  1301. flipBytes(partialEntryBytes, 0, 8);
  1302. }
  1303. // we are writing intermediate segments (there is more data after this
  1304. // segment), so the length is always 9.
  1305. partialEntryBytes[8] = (byte)9;
  1306. pos += 8;
  1307. segmentLen -= 8;
  1308. bout.write(partialEntryBytes);
  1309. }
  1310. // write the last segment (with slightly different rules)
  1311. if(segmentLen > 0) {
  1312. System.arraycopy(valueBytes, pos, partialEntryBytes, 0, segmentLen);
  1313. // clear out an intermediate bytes between the real data and the final
  1314. // length byte
  1315. for(int i = segmentLen; i < 8; ++i) {
  1316. partialEntryBytes[i] = 0;
  1317. }
  1318. partialEntryBytes[8] = (byte)segmentLen;
  1319. if(!isAsc) {
  1320. // note, we _do_ flip the last length byte
  1321. flipBytes(partialEntryBytes, 0, 9);
  1322. }
  1323. bout.write(partialEntryBytes);
  1324. }
  1325. }
  1326. /**
  1327. * Creates one of the special index entries.
  1328. */
  1329. private static Entry createSpecialEntry(RowIdImpl rowId) {
  1330. return new Entry((byte[])null, rowId);
  1331. }
  1332. /**
  1333. * Constructs a ColumnDescriptor of the relevant type for the given Column.
  1334. */
  1335. private ColumnDescriptor newColumnDescriptor(ColumnImpl col, byte flags)
  1336. throws IOException
  1337. {
  1338. switch(col.getType()) {
  1339. case TEXT:
  1340. case MEMO:
  1341. ColumnImpl.SortOrder sortOrder = col.getTextSortOrder();
  1342. if(ColumnImpl.GENERAL_SORT_ORDER.equals(sortOrder)) {
  1343. return new GenTextColumnDescriptor(col, flags);
  1344. }
  1345. if(ColumnImpl.GENERAL_LEGACY_SORT_ORDER.equals(sortOrder)) {
  1346. return new GenLegTextColumnDescriptor(col, flags);
  1347. }
  1348. if(ColumnImpl.GENERAL_97_SORT_ORDER.equals(sortOrder)) {
  1349. return new Gen97TextColumnDescriptor(col, flags);
  1350. }
  1351. // unsupported sort order
  1352. setUnsupportedReason("unsupported collating sort order " + sortOrder +
  1353. " for text index", col);
  1354. return new ReadOnlyColumnDescriptor(col, flags);
  1355. case INT:
  1356. case LONG:
  1357. case MONEY:
  1358. case COMPLEX_TYPE:
  1359. case BIG_INT:
  1360. return new IntegerColumnDescriptor(col, flags);
  1361. case FLOAT:
  1362. case DOUBLE:
  1363. case SHORT_DATE_TIME:
  1364. return new FloatingPointColumnDescriptor(col, flags);
  1365. case NUMERIC:
  1366. return (col.getFormat().LEGACY_NUMERIC_INDEXES ?
  1367. new LegacyFixedPointColumnDescriptor(col, flags) :
  1368. new FixedPointColumnDescriptor(col, flags));
  1369. case BYTE:
  1370. return new ByteColumnDescriptor(col, flags);
  1371. case BOOLEAN:
  1372. return new BooleanColumnDescriptor(col, flags);
  1373. case GUID:
  1374. return new GuidColumnDescriptor(col, flags);
  1375. case BINARY:
  1376. return new BinaryColumnDescriptor(col, flags);
  1377. case EXT_DATE_TIME:
  1378. return new ExtDateColumnDescriptor(col, flags);
  1379. default:
  1380. // we can't modify this index at this point in time
  1381. setUnsupportedReason("unsupported data type " + col.getType() +
  1382. " for index", col);
  1383. return new ReadOnlyColumnDescriptor(col, flags);
  1384. }
  1385. }
  1386. /**
  1387. * Returns the EntryType based on the given entry info.
  1388. */
  1389. private static EntryType determineEntryType(byte[] entryBytes, RowIdImpl rowId)
  1390. {
  1391. if(entryBytes != null) {
  1392. return ((rowId.getType() == RowIdImpl.Type.NORMAL) ?
  1393. EntryType.NORMAL :
  1394. ((rowId.getType() == RowIdImpl.Type.ALWAYS_FIRST) ?
  1395. EntryType.FIRST_VALID : EntryType.LAST_VALID));
  1396. } else if(!rowId.isValid()) {
  1397. // this is a "special" entry (first/last)
  1398. return ((rowId.getType() == RowIdImpl.Type.ALWAYS_FIRST) ?
  1399. EntryType.ALWAYS_FIRST : EntryType.ALWAYS_LAST);
  1400. }
  1401. throw new IllegalArgumentException("Values was null for valid entry");
  1402. }
  1403. /**
  1404. * Returns the maximum amount of entry data which can be encoded on any
  1405. * index page.
  1406. */
  1407. private static int calcMaxPageEntrySize(JetFormat format)
  1408. {
  1409. // the max data we can fit on a page is the min of the space on the page
  1410. // vs the number of bytes which can be encoded in the entry mask
  1411. int pageDataSize = (format.PAGE_SIZE -
  1412. (format.OFFSET_INDEX_ENTRY_MASK +
  1413. format.SIZE_INDEX_ENTRY_MASK));
  1414. int entryMaskSize = (format.SIZE_INDEX_ENTRY_MASK * 8);
  1415. return Math.min(pageDataSize, entryMaskSize);
  1416. }
  1417. String withErrorContext(String msg) {
  1418. return withErrorContext(msg, getTable().getDatabase(), getTable().getName(),
  1419. getName());
  1420. }
  1421. private static String withErrorContext(String msg, DatabaseImpl db,
  1422. String tableName, String idxName) {
  1423. return msg + " (Db=" + db.getName() + ";Table=" + tableName +
  1424. ";Index=" + idxName + ")";
  1425. }
  1426. /**
  1427. * Information about the columns in an index. Also encodes new index
  1428. * values.
  1429. */
  1430. public static abstract class ColumnDescriptor implements Index.Column
  1431. {
  1432. private final ColumnImpl _column;
  1433. private final byte _flags;
  1434. private ColumnDescriptor(ColumnImpl column, byte flags)
  1435. throws IOException
  1436. {
  1437. _column = column;
  1438. _flags = flags;
  1439. }
  1440. @Override
  1441. public ColumnImpl getColumn() {
  1442. return _column;
  1443. }
  1444. public byte getFlags() {
  1445. return _flags;
  1446. }
  1447. @Override
  1448. public boolean isAscending() {
  1449. return((getFlags() & ASCENDING_COLUMN_FLAG) != 0);
  1450. }
  1451. @Override
  1452. public int getColumnIndex() {
  1453. return getColumn().getColumnIndex();
  1454. }
  1455. @Override
  1456. public String getName() {
  1457. return getColumn().getName();
  1458. }
  1459. protected boolean isNullValue(Object value) {
  1460. return (value == null);
  1461. }
  1462. protected final void writeValue(Object value, ByteStream bout)
  1463. throws IOException
  1464. {
  1465. if(isNullValue(value)) {
  1466. // write null value
  1467. bout.write(getNullEntryFlag(isAscending()));
  1468. return;
  1469. }
  1470. // write the start flag
  1471. bout.write(getStartEntryFlag(isAscending()));
  1472. // write the rest of the value
  1473. writeNonNullValue(value, bout);
  1474. }
  1475. protected abstract void writeNonNullValue(Object value, ByteStream bout)
  1476. throws IOException;
  1477. @Override
  1478. public String toString() {
  1479. return CustomToStringStyle.builder(this)
  1480. .append("column", getColumn())
  1481. .append("flags", getFlags() + " " + (isAscending() ? "(ASC)" : "(DSC)"))
  1482. .toString();
  1483. }
  1484. }
  1485. /**
  1486. * ColumnDescriptor for integer based columns.
  1487. */
  1488. private static final class IntegerColumnDescriptor extends ColumnDescriptor
  1489. {
  1490. private IntegerColumnDescriptor(ColumnImpl column, byte flags)
  1491. throws IOException
  1492. {
  1493. super(column, flags);
  1494. }
  1495. @Override
  1496. protected void writeNonNullValue(Object value, ByteStream bout)
  1497. throws IOException
  1498. {
  1499. byte[] valueBytes = encodeNumberColumnValue(value, getColumn());
  1500. // bit twiddling rules:
  1501. // - isAsc => flipFirstBit
  1502. // - !isAsc => flipFirstBit, flipBytes
  1503. flipFirstBitInByte(valueBytes, 0);
  1504. if(!isAscending()) {
  1505. flipBytes(valueBytes);
  1506. }
  1507. bout.write(valueBytes);
  1508. }
  1509. }
  1510. /**
  1511. * ColumnDescriptor for floating point based columns.
  1512. */
  1513. private static final class FloatingPointColumnDescriptor
  1514. extends ColumnDescriptor
  1515. {
  1516. private FloatingPointColumnDescriptor(ColumnImpl column, byte flags)
  1517. throws IOException
  1518. {
  1519. super(column, flags);
  1520. }
  1521. @Override
  1522. protected void writeNonNullValue(Object value, ByteStream bout)
  1523. throws IOException
  1524. {
  1525. byte[] valueBytes = encodeNumberColumnValue(value, getColumn());
  1526. // determine if the number is negative by testing if the first bit is
  1527. // set
  1528. boolean isNegative = ((valueBytes[0] & 0x80) != 0);
  1529. // bit twiddling rules:
  1530. // isAsc && !isNeg => flipFirstBit
  1531. // isAsc && isNeg => flipBytes
  1532. // !isAsc && !isNeg => flipFirstBit, flipBytes
  1533. // !isAsc && isNeg => nothing
  1534. if(!isNegative) {
  1535. flipFirstBitInByte(valueBytes, 0);
  1536. }
  1537. if(isNegative == isAscending()) {
  1538. flipBytes(valueBytes);
  1539. }
  1540. bout.write(valueBytes);
  1541. }
  1542. }
  1543. /**
  1544. * ColumnDescriptor for fixed point based columns (legacy sort order).
  1545. */
  1546. private static class LegacyFixedPointColumnDescriptor
  1547. extends ColumnDescriptor
  1548. {
  1549. private LegacyFixedPointColumnDescriptor(ColumnImpl column, byte flags)
  1550. throws IOException
  1551. {
  1552. super(column, flags);
  1553. }
  1554. protected void handleNegationAndOrder(boolean isNegative,
  1555. byte[] valueBytes)
  1556. {
  1557. if(isNegative == isAscending()) {
  1558. flipBytes(valueBytes);
  1559. }
  1560. // reverse the sign byte (after any previous byte flipping)
  1561. valueBytes[0] = (isNegative ? (byte)0x00 : (byte)0xFF);
  1562. }
  1563. @Override
  1564. protected void writeNonNullValue(Object value, ByteStream bout)
  1565. throws IOException
  1566. {
  1567. byte[] valueBytes = encodeNumberColumnValue(value, getColumn());
  1568. // determine if the number is negative by testing if the first bit is
  1569. // set
  1570. boolean isNegative = ((valueBytes[0] & 0x80) != 0);
  1571. // bit twiddling rules:
  1572. // isAsc && !isNeg => setReverseSignByte => FF 00 00 ...
  1573. // isAsc && isNeg => flipBytes, setReverseSignByte => 00 FF FF ...
  1574. // !isAsc && !isNeg => flipBytes, setReverseSignByte => FF FF FF ...
  1575. // !isAsc && isNeg => setReverseSignByte => 00 00 00 ...
  1576. // v2007 bit twiddling rules (old ordering was a bug, MS kb 837148):
  1577. // isAsc && !isNeg => setSignByte 0xFF => FF 00 00 ...
  1578. // isAsc && isNeg => setSignByte 0xFF, flipBytes => 00 FF FF ...
  1579. // !isAsc && !isNeg => setSignByte 0xFF => FF 00 00 ...
  1580. // !isAsc && isNeg => setSignByte 0xFF, flipBytes => 00 FF FF ...
  1581. handleNegationAndOrder(isNegative, valueBytes);
  1582. bout.write(valueBytes);
  1583. }
  1584. }
  1585. /**
  1586. * ColumnDescriptor for new-style fixed point based columns.
  1587. */
  1588. private static final class FixedPointColumnDescriptor
  1589. extends LegacyFixedPointColumnDescriptor
  1590. {
  1591. private FixedPointColumnDescriptor(ColumnImpl column, byte flags)
  1592. throws IOException
  1593. {
  1594. super(column, flags);
  1595. }
  1596. @Override
  1597. protected void handleNegationAndOrder(boolean isNegative,
  1598. byte[] valueBytes)
  1599. {
  1600. // see notes above in FixedPointColumnDescriptor for bit twiddling rules
  1601. // reverse the sign byte (before any byte flipping)
  1602. valueBytes[0] = (byte)0xFF;
  1603. if(isNegative == isAscending()) {
  1604. flipBytes(valueBytes);
  1605. }
  1606. }
  1607. }
  1608. /**
  1609. * ColumnDescriptor for byte based columns.
  1610. */
  1611. private static final class ByteColumnDescriptor extends ColumnDescriptor
  1612. {
  1613. private ByteColumnDescriptor(ColumnImpl column, byte flags)
  1614. throws IOException
  1615. {
  1616. super(column, flags);
  1617. }
  1618. @Override
  1619. protected void writeNonNullValue(Object value, ByteStream bout)
  1620. throws IOException
  1621. {
  1622. byte[] valueBytes = encodeNumberColumnValue(value, getColumn());
  1623. // bit twiddling rules:
  1624. // - isAsc => nothing
  1625. // - !isAsc => flipBytes
  1626. if(!isAscending()) {
  1627. flipBytes(valueBytes);
  1628. }
  1629. bout.write(valueBytes);
  1630. }
  1631. }
  1632. /**
  1633. * ColumnDescriptor for boolean columns.
  1634. */
  1635. private static final class BooleanColumnDescriptor extends ColumnDescriptor
  1636. {
  1637. private BooleanColumnDescriptor(ColumnImpl column, byte flags)
  1638. throws IOException
  1639. {
  1640. super(column, flags);
  1641. }
  1642. @Override
  1643. protected boolean isNullValue(Object value) {
  1644. // null values are handled as booleans
  1645. return false;
  1646. }
  1647. @Override
  1648. protected void writeNonNullValue(Object value, ByteStream bout)
  1649. throws IOException
  1650. {
  1651. bout.write(
  1652. ColumnImpl.toBooleanValue(value) ?
  1653. (isAscending() ? ASC_BOOLEAN_TRUE : DESC_BOOLEAN_TRUE) :
  1654. (isAscending() ? ASC_BOOLEAN_FALSE : DESC_BOOLEAN_FALSE));
  1655. }
  1656. }
  1657. /**
  1658. * ColumnDescriptor for "general legacy" sort order text based columns.
  1659. */
  1660. private static final class GenLegTextColumnDescriptor
  1661. extends ColumnDescriptor
  1662. {
  1663. private GenLegTextColumnDescriptor(ColumnImpl column, byte flags)
  1664. throws IOException
  1665. {
  1666. super(column, flags);
  1667. }
  1668. @Override
  1669. protected void writeNonNullValue(Object value, ByteStream bout)
  1670. throws IOException
  1671. {
  1672. GeneralLegacyIndexCodes.GEN_LEG_INSTANCE.writeNonNullIndexTextValue(
  1673. value, bout, isAscending());
  1674. }
  1675. }
  1676. /**
  1677. * ColumnDescriptor for "general" sort order (2010+) text based columns.
  1678. */
  1679. private static final class GenTextColumnDescriptor extends ColumnDescriptor
  1680. {
  1681. private GenTextColumnDescriptor(ColumnImpl column, byte flags)
  1682. throws IOException
  1683. {
  1684. super(column, flags);
  1685. }
  1686. @Override
  1687. protected void writeNonNullValue(Object value, ByteStream bout)
  1688. throws IOException
  1689. {
  1690. GeneralIndexCodes.GEN_INSTANCE.writeNonNullIndexTextValue(
  1691. value, bout, isAscending());
  1692. }
  1693. }
  1694. /**
  1695. * ColumnDescriptor for "general 97" sort order text based columns.
  1696. */
  1697. private static final class Gen97TextColumnDescriptor
  1698. extends ColumnDescriptor
  1699. {
  1700. private Gen97TextColumnDescriptor(ColumnImpl column, byte flags)
  1701. throws IOException
  1702. {
  1703. super(column, flags);
  1704. }
  1705. @Override
  1706. protected void writeNonNullValue(Object value, ByteStream bout)
  1707. throws IOException
  1708. {
  1709. General97IndexCodes.GEN_97_INSTANCE.writeNonNullIndexTextValue(
  1710. value, bout, isAscending());
  1711. }
  1712. }
  1713. /**
  1714. * ColumnDescriptor for guid columns.
  1715. */
  1716. private static final class GuidColumnDescriptor extends ColumnDescriptor
  1717. {
  1718. private GuidColumnDescriptor(ColumnImpl column, byte flags)
  1719. throws IOException
  1720. {
  1721. super(column, flags);
  1722. }
  1723. @Override
  1724. protected void writeNonNullValue(Object value, ByteStream bout)
  1725. throws IOException
  1726. {
  1727. writeGeneralBinaryEntry(
  1728. encodeNumberColumnValue(value, getColumn()), isAscending(),
  1729. bout);
  1730. }
  1731. }
  1732. /**
  1733. * ColumnDescriptor for BINARY columns.
  1734. */
  1735. private static final class BinaryColumnDescriptor extends ColumnDescriptor
  1736. {
  1737. private BinaryColumnDescriptor(ColumnImpl column, byte flags)
  1738. throws IOException
  1739. {
  1740. super(column, flags);
  1741. }
  1742. @Override
  1743. protected void writeNonNullValue(Object value, ByteStream bout)
  1744. throws IOException
  1745. {
  1746. writeGeneralBinaryEntry(
  1747. ColumnImpl.toByteArray(value), isAscending(), bout);
  1748. }
  1749. }
  1750. /**
  1751. * ColumnDescriptor for extended date/time based columns.
  1752. */
  1753. private static final class ExtDateColumnDescriptor extends ColumnDescriptor
  1754. {
  1755. private ExtDateColumnDescriptor(ColumnImpl column, byte flags)
  1756. throws IOException
  1757. {
  1758. super(column, flags);
  1759. }
  1760. @Override
  1761. protected void writeNonNullValue(Object value, ByteStream bout)
  1762. throws IOException
  1763. {
  1764. byte[] valueBytes = encodeNumberColumnValue(value, getColumn());
  1765. // entry (which is 42 bytes of data) is encoded in blocks of 8 bytes
  1766. // separated by '\t' char
  1767. // note that for desc, all bytes are flipped _except_ separator char
  1768. byte[] trailer = ASC_EXT_DATE_TRAILER;
  1769. if(!isAscending()) {
  1770. flipBytes(valueBytes);
  1771. trailer = DESC_EXT_DATE_TRAILER;
  1772. }
  1773. // first 5 blocks are all value data
  1774. int valIdx = 0;
  1775. for(int i = 0; i < 5; ++i) {
  1776. bout.write(valueBytes, valIdx, 8);
  1777. bout.write((byte)0x09);
  1778. valIdx += 8;
  1779. }
  1780. // last two data bytes and then the trailer
  1781. bout.write(valueBytes, valIdx, 2);
  1782. bout.write(trailer);
  1783. }
  1784. }
  1785. /**
  1786. * ColumnDescriptor for columns which we cannot currently write.
  1787. */
  1788. private final class ReadOnlyColumnDescriptor extends ColumnDescriptor
  1789. {
  1790. private ReadOnlyColumnDescriptor(ColumnImpl column, byte flags)
  1791. throws IOException
  1792. {
  1793. super(column, flags);
  1794. }
  1795. @Override
  1796. protected void writeNonNullValue(Object value, ByteStream bout)
  1797. throws IOException
  1798. {
  1799. throw new UnsupportedOperationException(
  1800. "Cannot write indexes of this type due to " + _unsupportedReason);
  1801. }
  1802. }
  1803. /**
  1804. * A single leaf entry in an index (points to a single row)
  1805. */
  1806. public static class Entry implements Comparable<Entry>
  1807. {
  1808. /** page/row on which this row is stored */
  1809. private final RowIdImpl _rowId;
  1810. /** the entry value */
  1811. private final byte[] _entryBytes;
  1812. /** comparable type for the entry */
  1813. private final EntryType _type;
  1814. /**
  1815. * Create a new entry
  1816. * @param entryBytes encoded bytes for this index entry
  1817. * @param rowId rowId in which the row is stored
  1818. * @param type the type of the entry
  1819. */
  1820. private Entry(byte[] entryBytes, RowIdImpl rowId, EntryType type) {
  1821. _rowId = rowId;
  1822. _entryBytes = entryBytes;
  1823. _type = type;
  1824. }
  1825. /**
  1826. * Create a new entry
  1827. * @param entryBytes encoded bytes for this index entry
  1828. * @param rowId rowId in which the row is stored
  1829. */
  1830. private Entry(byte[] entryBytes, RowIdImpl rowId)
  1831. {
  1832. this(entryBytes, rowId, determineEntryType(entryBytes, rowId));
  1833. }
  1834. /**
  1835. * Read an existing entry in from a buffer
  1836. */
  1837. private Entry(ByteBuffer buffer, int entryLen)
  1838. throws IOException
  1839. {
  1840. this(buffer, entryLen, 0);
  1841. }
  1842. /**
  1843. * Read an existing entry in from a buffer
  1844. */
  1845. private Entry(ByteBuffer buffer, int entryLen, int extraTrailingLen)
  1846. throws IOException
  1847. {
  1848. // we need 4 trailing bytes for the rowId, plus whatever the caller
  1849. // wants
  1850. int colEntryLen = entryLen - (4 + extraTrailingLen);
  1851. // read the entry bytes
  1852. _entryBytes = ByteUtil.getBytes(buffer, colEntryLen);
  1853. // read the rowId
  1854. int page = ByteUtil.get3ByteInt(buffer, ENTRY_BYTE_ORDER);
  1855. int row = ByteUtil.getUnsignedByte(buffer);
  1856. _rowId = new RowIdImpl(page, row);
  1857. _type = EntryType.NORMAL;
  1858. }
  1859. public RowIdImpl getRowId() {
  1860. return _rowId;
  1861. }
  1862. public EntryType getType() {
  1863. return _type;
  1864. }
  1865. public Integer getSubPageNumber() {
  1866. throw new UnsupportedOperationException();
  1867. }
  1868. public boolean isLeafEntry() {
  1869. return true;
  1870. }
  1871. public boolean isValid() {
  1872. return(_entryBytes != null);
  1873. }
  1874. protected final byte[] getEntryBytes() {
  1875. return _entryBytes;
  1876. }
  1877. /**
  1878. * Size of this entry in the db.
  1879. */
  1880. protected int size() {
  1881. // need 4 trailing bytes for the rowId
  1882. return _entryBytes.length + 4;
  1883. }
  1884. /**
  1885. * Write this entry into a buffer
  1886. */
  1887. protected void write(ByteBuffer buffer,
  1888. byte[] prefix)
  1889. throws IOException
  1890. {
  1891. if(prefix.length <= _entryBytes.length) {
  1892. // write entry bytes, not including prefix
  1893. buffer.put(_entryBytes, prefix.length,
  1894. (_entryBytes.length - prefix.length));
  1895. ByteUtil.put3ByteInt(buffer, getRowId().getPageNumber(),
  1896. ENTRY_BYTE_ORDER);
  1897. } else if(prefix.length <= (_entryBytes.length + 3)) {
  1898. // the prefix includes part of the page number, write to temp buffer
  1899. // and copy last bytes to output buffer
  1900. ByteBuffer tmp = ByteBuffer.allocate(3);
  1901. ByteUtil.put3ByteInt(tmp, getRowId().getPageNumber(),
  1902. ENTRY_BYTE_ORDER);
  1903. tmp.flip();
  1904. tmp.position(prefix.length - _entryBytes.length);
  1905. buffer.put(tmp);
  1906. } else {
  1907. // since the row number would never be the same if the page number is
  1908. // the same, nothing past the page number should ever be included in
  1909. // the prefix.
  1910. // FIXME, this could happen if page has only one row...
  1911. throw new IllegalStateException("prefix should never be this long");
  1912. }
  1913. buffer.put((byte)getRowId().getRowNumber());
  1914. }
  1915. protected final ToStringBuilder entryBytesToStringBuilder(
  1916. ToStringBuilder sb) {
  1917. if(isValid()) {
  1918. sb.append("bytes", _entryBytes);
  1919. }
  1920. return sb;
  1921. }
  1922. @Override
  1923. public String toString() {
  1924. return entryBytesToStringBuilder(
  1925. CustomToStringStyle.valueBuilder(this)
  1926. .append("rowId", _rowId))
  1927. .toString();
  1928. }
  1929. @Override
  1930. public int hashCode() {
  1931. return _rowId.hashCode();
  1932. }
  1933. @Override
  1934. public boolean equals(Object o) {
  1935. return((this == o) ||
  1936. ((o != null) && (getClass() == o.getClass()) &&
  1937. (compareTo((Entry)o) == 0)));
  1938. }
  1939. /**
  1940. * @return {@code true} iff the entryBytes are equal between this
  1941. * Entry and the given Entry
  1942. */
  1943. public boolean equalsEntryBytes(Entry o) {
  1944. return(BYTE_CODE_COMPARATOR.compare(_entryBytes, o._entryBytes) == 0);
  1945. }
  1946. @Override
  1947. public int compareTo(Entry other) {
  1948. if (this == other) {
  1949. return 0;
  1950. }
  1951. if(isValid() && other.isValid()) {
  1952. // comparing two valid entries. first, compare by actual byte values
  1953. int entryCmp = BYTE_CODE_COMPARATOR.compare(
  1954. _entryBytes, other._entryBytes);
  1955. if(entryCmp != 0) {
  1956. return entryCmp;
  1957. }
  1958. } else {
  1959. // if the entries are of mixed validity (or both invalid), we defer
  1960. // next to the EntryType
  1961. int typeCmp = _type.compareTo(other._type);
  1962. if(typeCmp != 0) {
  1963. return typeCmp;
  1964. }
  1965. }
  1966. // at this point we let the RowId decide the final result
  1967. return _rowId.compareTo(other.getRowId());
  1968. }
  1969. /**
  1970. * Returns a copy of this entry as a node Entry with the given
  1971. * subPageNumber.
  1972. */
  1973. protected Entry asNodeEntry(Integer subPageNumber) {
  1974. return new NodeEntry(_entryBytes, _rowId, _type, subPageNumber);
  1975. }
  1976. }
  1977. /**
  1978. * A single node entry in an index (points to a sub-page in the index)
  1979. */
  1980. private static final class NodeEntry extends Entry {
  1981. /** index page number of the page to which this node entry refers */
  1982. private final Integer _subPageNumber;
  1983. /**
  1984. * Create a new node entry
  1985. * @param entryBytes encoded bytes for this index entry
  1986. * @param rowId rowId in which the row is stored
  1987. * @param type the type of the entry
  1988. * @param subPageNumber the sub-page to which this node entry refers
  1989. */
  1990. private NodeEntry(byte[] entryBytes, RowIdImpl rowId, EntryType type,
  1991. Integer subPageNumber) {
  1992. super(entryBytes, rowId, type);
  1993. _subPageNumber = subPageNumber;
  1994. }
  1995. /**
  1996. * Read an existing node entry in from a buffer
  1997. */
  1998. private NodeEntry(ByteBuffer buffer, int entryLen)
  1999. throws IOException
  2000. {
  2001. // we need 4 trailing bytes for the sub-page number
  2002. super(buffer, entryLen, 4);
  2003. _subPageNumber = ByteUtil.getInt(buffer, ENTRY_BYTE_ORDER);
  2004. }
  2005. @Override
  2006. public Integer getSubPageNumber() {
  2007. return _subPageNumber;
  2008. }
  2009. @Override
  2010. public boolean isLeafEntry() {
  2011. return false;
  2012. }
  2013. @Override
  2014. protected int size() {
  2015. // need 4 trailing bytes for the sub-page number
  2016. return super.size() + 4;
  2017. }
  2018. @Override
  2019. protected void write(ByteBuffer buffer, byte[] prefix) throws IOException {
  2020. super.write(buffer, prefix);
  2021. ByteUtil.putInt(buffer, _subPageNumber, ENTRY_BYTE_ORDER);
  2022. }
  2023. @Override
  2024. public boolean equals(Object o) {
  2025. return((this == o) ||
  2026. ((o != null) && (getClass() == o.getClass()) &&
  2027. (compareTo((Entry)o) == 0) &&
  2028. (getSubPageNumber().equals(((Entry)o).getSubPageNumber()))));
  2029. }
  2030. @Override
  2031. public String toString() {
  2032. return entryBytesToStringBuilder(
  2033. CustomToStringStyle.valueBuilder(this)
  2034. .append("rowId", getRowId())
  2035. .append("subPage", _subPageNumber))
  2036. .toString();
  2037. }
  2038. }
  2039. /**
  2040. * Utility class to traverse the entries in the Index. Remains valid in the
  2041. * face of index entry modifications.
  2042. */
  2043. public final class EntryCursor
  2044. {
  2045. /** handler for moving the page cursor forward */
  2046. private final DirHandler _forwardDirHandler = new ForwardDirHandler();
  2047. /** handler for moving the page cursor backward */
  2048. private final DirHandler _reverseDirHandler = new ReverseDirHandler();
  2049. /** the first (exclusive) row id for this cursor */
  2050. private Position _firstPos;
  2051. /** the last (exclusive) row id for this cursor */
  2052. private Position _lastPos;
  2053. /** the current entry */
  2054. private Position _curPos;
  2055. /** the previous entry */
  2056. private Position _prevPos;
  2057. /** the last read modification count on the Index. we track this so that
  2058. the cursor can detect updates to the index while traversing and act
  2059. accordingly */
  2060. private int _lastModCount;
  2061. private EntryCursor(Position firstPos, Position lastPos)
  2062. {
  2063. _firstPos = firstPos;
  2064. _lastPos = lastPos;
  2065. _lastModCount = getIndexModCount();
  2066. reset();
  2067. }
  2068. /**
  2069. * Returns the DirHandler for the given direction
  2070. */
  2071. private DirHandler getDirHandler(boolean moveForward) {
  2072. return (moveForward ? _forwardDirHandler : _reverseDirHandler);
  2073. }
  2074. public IndexData getIndexData() {
  2075. return IndexData.this;
  2076. }
  2077. private int getIndexModCount() {
  2078. return IndexData.this._modCount;
  2079. }
  2080. /**
  2081. * Returns the first entry (exclusive) as defined by this cursor.
  2082. */
  2083. public Entry getFirstEntry() {
  2084. return _firstPos.getEntry();
  2085. }
  2086. /**
  2087. * Returns the last entry (exclusive) as defined by this cursor.
  2088. */
  2089. public Entry getLastEntry() {
  2090. return _lastPos.getEntry();
  2091. }
  2092. /**
  2093. * Returns {@code true} if this cursor is up-to-date with respect to its
  2094. * index.
  2095. */
  2096. public boolean isUpToDate() {
  2097. return(getIndexModCount() == _lastModCount);
  2098. }
  2099. public void reset() {
  2100. beforeFirst();
  2101. }
  2102. public void beforeFirst() {
  2103. reset(CursorImpl.MOVE_FORWARD);
  2104. }
  2105. public void afterLast() {
  2106. reset(CursorImpl.MOVE_REVERSE);
  2107. }
  2108. protected void reset(boolean moveForward)
  2109. {
  2110. _curPos = getDirHandler(moveForward).getBeginningPosition();
  2111. _prevPos = _curPos;
  2112. }
  2113. /**
  2114. * Repositions the cursor so that the next row will be the first entry
  2115. * &gt;= the given row.
  2116. */
  2117. public void beforeEntry(Object[] row)
  2118. throws IOException
  2119. {
  2120. restorePosition(new Entry(IndexData.this.createEntryBytes(row),
  2121. RowIdImpl.FIRST_ROW_ID));
  2122. }
  2123. /**
  2124. * Repositions the cursor so that the previous row will be the first
  2125. * entry &lt;= the given row.
  2126. */
  2127. public void afterEntry(Object[] row)
  2128. throws IOException
  2129. {
  2130. restorePosition(new Entry(IndexData.this.createEntryBytes(row),
  2131. RowIdImpl.LAST_ROW_ID));
  2132. }
  2133. /**
  2134. * @return valid entry if there was a next entry,
  2135. * {@code #getLastEntry} otherwise
  2136. */
  2137. public Entry getNextEntry() throws IOException {
  2138. return getAnotherPosition(CursorImpl.MOVE_FORWARD).getEntry();
  2139. }
  2140. /**
  2141. * @return valid entry if there was a next entry,
  2142. * {@code #getFirstEntry} otherwise
  2143. */
  2144. public Entry getPreviousEntry() throws IOException {
  2145. return getAnotherPosition(CursorImpl.MOVE_REVERSE).getEntry();
  2146. }
  2147. /**
  2148. * Restores a current position for the cursor (current position becomes
  2149. * previous position).
  2150. */
  2151. protected void restorePosition(Entry curEntry)
  2152. throws IOException
  2153. {
  2154. restorePosition(curEntry, _curPos.getEntry());
  2155. }
  2156. /**
  2157. * Restores a current and previous position for the cursor.
  2158. */
  2159. protected void restorePosition(Entry curEntry, Entry prevEntry)
  2160. throws IOException
  2161. {
  2162. if(!_curPos.equalsEntry(curEntry) ||
  2163. !_prevPos.equalsEntry(prevEntry))
  2164. {
  2165. if(!isUpToDate()) {
  2166. updateBounds();
  2167. _lastModCount = getIndexModCount();
  2168. }
  2169. _prevPos = updatePosition(prevEntry);
  2170. _curPos = updatePosition(curEntry);
  2171. } else {
  2172. checkForModification();
  2173. }
  2174. }
  2175. /**
  2176. * Gets another entry in the given direction, returning the new entry.
  2177. */
  2178. private Position getAnotherPosition(boolean moveForward)
  2179. throws IOException
  2180. {
  2181. DirHandler handler = getDirHandler(moveForward);
  2182. if(_curPos.equals(handler.getEndPosition())) {
  2183. if(!isUpToDate()) {
  2184. restorePosition(_prevPos.getEntry());
  2185. // drop through and retry moving to another entry
  2186. } else {
  2187. // at end, no more
  2188. return _curPos;
  2189. }
  2190. }
  2191. checkForModification();
  2192. _prevPos = _curPos;
  2193. _curPos = handler.getAnotherPosition(_curPos);
  2194. return _curPos;
  2195. }
  2196. /**
  2197. * Checks the index for modifications and updates state accordingly.
  2198. */
  2199. private void checkForModification()
  2200. throws IOException
  2201. {
  2202. if(!isUpToDate()) {
  2203. updateBounds();
  2204. _prevPos = updatePosition(_prevPos.getEntry());
  2205. _curPos = updatePosition(_curPos.getEntry());
  2206. _lastModCount = getIndexModCount();
  2207. }
  2208. }
  2209. /**
  2210. * Updates the given position, taking boundaries into account.
  2211. */
  2212. private Position updatePosition(Entry entry)
  2213. throws IOException
  2214. {
  2215. if(!entry.isValid()) {
  2216. // no use searching if "updating" the first/last pos
  2217. if(_firstPos.equalsEntry(entry)) {
  2218. return _firstPos;
  2219. } else if(_lastPos.equalsEntry(entry)) {
  2220. return _lastPos;
  2221. } else {
  2222. throw new IllegalArgumentException(
  2223. withErrorContext("Invalid entry given " + entry));
  2224. }
  2225. }
  2226. Position pos = findEntryPosition(entry);
  2227. if(pos.compareTo(_lastPos) >= 0) {
  2228. return _lastPos;
  2229. } else if(pos.compareTo(_firstPos) <= 0) {
  2230. return _firstPos;
  2231. }
  2232. return pos;
  2233. }
  2234. /**
  2235. * Updates any the boundary info (_firstPos/_lastPos).
  2236. */
  2237. private void updateBounds()
  2238. throws IOException
  2239. {
  2240. _firstPos = findEntryPosition(_firstPos.getEntry());
  2241. _lastPos = findEntryPosition(_lastPos.getEntry());
  2242. }
  2243. @Override
  2244. public String toString() {
  2245. return CustomToStringStyle.valueBuilder(this)
  2246. .append("curPosition", _curPos)
  2247. .append("prevPosition", _prevPos)
  2248. .toString();
  2249. }
  2250. /**
  2251. * Handles moving the cursor in a given direction. Separates cursor
  2252. * logic from value storage.
  2253. */
  2254. private abstract class DirHandler {
  2255. public abstract Position getAnotherPosition(Position curPos)
  2256. throws IOException;
  2257. public abstract Position getBeginningPosition();
  2258. public abstract Position getEndPosition();
  2259. }
  2260. /**
  2261. * Handles moving the cursor forward.
  2262. */
  2263. private final class ForwardDirHandler extends DirHandler {
  2264. @Override
  2265. public Position getAnotherPosition(Position curPos)
  2266. throws IOException
  2267. {
  2268. Position newPos = getNextPosition(curPos);
  2269. if((newPos == null) || (newPos.compareTo(_lastPos) >= 0)) {
  2270. newPos = _lastPos;
  2271. }
  2272. return newPos;
  2273. }
  2274. @Override
  2275. public Position getBeginningPosition() {
  2276. return _firstPos;
  2277. }
  2278. @Override
  2279. public Position getEndPosition() {
  2280. return _lastPos;
  2281. }
  2282. }
  2283. /**
  2284. * Handles moving the cursor backward.
  2285. */
  2286. private final class ReverseDirHandler extends DirHandler {
  2287. @Override
  2288. public Position getAnotherPosition(Position curPos)
  2289. throws IOException
  2290. {
  2291. Position newPos = getPreviousPosition(curPos);
  2292. if((newPos == null) || (newPos.compareTo(_firstPos) <= 0)) {
  2293. newPos = _firstPos;
  2294. }
  2295. return newPos;
  2296. }
  2297. @Override
  2298. public Position getBeginningPosition() {
  2299. return _lastPos;
  2300. }
  2301. @Override
  2302. public Position getEndPosition() {
  2303. return _firstPos;
  2304. }
  2305. }
  2306. }
  2307. /**
  2308. * Simple value object for maintaining some cursor state.
  2309. */
  2310. private static final class Position implements Comparable<Position> {
  2311. /** the last known page of the given entry */
  2312. private final DataPage _dataPage;
  2313. /** the last known index of the given entry */
  2314. private final int _idx;
  2315. /** the entry at the given index */
  2316. private final Entry _entry;
  2317. /** {@code true} if this entry does not currently exist in the entry list,
  2318. {@code false} otherwise (this is equivalent to adding -0.5 to the
  2319. _idx) */
  2320. private final boolean _between;
  2321. private Position(DataPage dataPage, int idx)
  2322. {
  2323. this(dataPage, idx, dataPage.getEntries().get(idx), false);
  2324. }
  2325. private Position(DataPage dataPage, int idx, Entry entry, boolean between)
  2326. {
  2327. _dataPage = dataPage;
  2328. _idx = idx;
  2329. _entry = entry;
  2330. _between = between;
  2331. }
  2332. DataPage getDataPage() {
  2333. return _dataPage;
  2334. }
  2335. int getIndex() {
  2336. return _idx;
  2337. }
  2338. int getNextIndex() {
  2339. // note, _idx does not need to be advanced if it was pointing at a
  2340. // between position
  2341. return(_between ? _idx : (_idx + 1));
  2342. }
  2343. int getPrevIndex() {
  2344. // note, we ignore the between flag here because the index will be
  2345. // pointing at the correct next index in either the between or
  2346. // non-between case
  2347. return(_idx - 1);
  2348. }
  2349. Entry getEntry() {
  2350. return _entry;
  2351. }
  2352. boolean equalsEntry(Entry entry) {
  2353. return _entry.equals(entry);
  2354. }
  2355. @Override
  2356. public int compareTo(Position other)
  2357. {
  2358. if(this == other) {
  2359. return 0;
  2360. }
  2361. if(_dataPage.equals(other._dataPage)) {
  2362. // "simple" index comparison (handle between-ness)
  2363. int idxCmp = ((_idx < other._idx) ? -1 :
  2364. ((_idx > other._idx) ? 1 :
  2365. ((_between == other._between) ? 0 :
  2366. (_between ? -1 : 1))));
  2367. if(idxCmp != 0) {
  2368. return idxCmp;
  2369. }
  2370. }
  2371. // compare the entries.
  2372. return _entry.compareTo(other._entry);
  2373. }
  2374. @Override
  2375. public int hashCode() {
  2376. return _entry.hashCode();
  2377. }
  2378. @Override
  2379. public boolean equals(Object o) {
  2380. return((this == o) ||
  2381. ((o != null) && (getClass() == o.getClass()) &&
  2382. (compareTo((Position)o) == 0)));
  2383. }
  2384. @Override
  2385. public String toString() {
  2386. return CustomToStringStyle.valueBuilder(this)
  2387. .append("page", _dataPage.getPageNumber())
  2388. .append("idx", _idx)
  2389. .append("entry", _entry)
  2390. .append("between", _between)
  2391. .toString();
  2392. }
  2393. }
  2394. /**
  2395. * Object used to maintain state about an Index page.
  2396. */
  2397. protected static abstract class DataPage {
  2398. public abstract int getPageNumber();
  2399. public abstract boolean isLeaf();
  2400. public abstract void setLeaf(boolean isLeaf);
  2401. public abstract int getPrevPageNumber();
  2402. public abstract void setPrevPageNumber(int pageNumber);
  2403. public abstract int getNextPageNumber();
  2404. public abstract void setNextPageNumber(int pageNumber);
  2405. public abstract int getChildTailPageNumber();
  2406. public abstract void setChildTailPageNumber(int pageNumber);
  2407. public abstract int getTotalEntrySize();
  2408. public abstract void setTotalEntrySize(int totalSize);
  2409. public abstract byte[] getEntryPrefix();
  2410. public abstract void setEntryPrefix(byte[] entryPrefix);
  2411. public abstract List<Entry> getEntries();
  2412. public abstract void setEntries(List<Entry> entries);
  2413. public abstract void addEntry(int idx, Entry entry)
  2414. throws IOException;
  2415. public abstract Entry removeEntry(int idx)
  2416. throws IOException;
  2417. public final boolean isEmpty() {
  2418. return getEntries().isEmpty();
  2419. }
  2420. public final int getCompressedEntrySize() {
  2421. // when written to the index page, the entryPrefix bytes will only be
  2422. // written for the first entry, so we subtract the entry prefix size
  2423. // from all the other entries to determine the compressed size
  2424. return getTotalEntrySize() -
  2425. (getEntryPrefix().length * (getEntries().size() - 1));
  2426. }
  2427. public final int findEntry(Entry entry) {
  2428. return Collections.binarySearch(getEntries(), entry);
  2429. }
  2430. @Override
  2431. public final int hashCode() {
  2432. return getPageNumber();
  2433. }
  2434. @Override
  2435. public final boolean equals(Object o) {
  2436. return((this == o) ||
  2437. ((o != null) && (getClass() == o.getClass()) &&
  2438. (getPageNumber() == ((DataPage)o).getPageNumber())));
  2439. }
  2440. @Override
  2441. public final String toString() {
  2442. List<Entry> entries = getEntries();
  2443. String objName =
  2444. (isLeaf() ? "Leaf" : "Node") + "DataPage[" + getPageNumber() +
  2445. "] " + getPrevPageNumber() + ", " + getNextPageNumber() + ", (" +
  2446. getChildTailPageNumber() + ")";
  2447. ToStringBuilder sb = CustomToStringStyle.valueBuilder(objName);
  2448. if((isLeaf() && !entries.isEmpty())) {
  2449. sb.append("entryRange", "[" + entries.get(0) + ", " +
  2450. entries.get(entries.size() - 1) + "]");
  2451. } else {
  2452. sb.append("entries", entries);
  2453. }
  2454. return sb.toString();
  2455. }
  2456. }
  2457. /**
  2458. * Simple implementation of a DataPage
  2459. */
  2460. private static final class RootDataPage extends DataPage {
  2461. @Override
  2462. public int getPageNumber() { return 0; }
  2463. @Override
  2464. public boolean isLeaf() { return true; }
  2465. @Override
  2466. public void setLeaf(boolean isLeaf) { }
  2467. @Override
  2468. public int getPrevPageNumber() { return 0; }
  2469. @Override
  2470. public void setPrevPageNumber(int pageNumber) { }
  2471. @Override
  2472. public int getNextPageNumber() { return 0; }
  2473. @Override
  2474. public void setNextPageNumber(int pageNumber) { }
  2475. @Override
  2476. public int getChildTailPageNumber() { return 0; }
  2477. @Override
  2478. public void setChildTailPageNumber(int pageNumber) { }
  2479. @Override
  2480. public int getTotalEntrySize() { return 0; }
  2481. @Override
  2482. public void setTotalEntrySize(int totalSize) { }
  2483. @Override
  2484. public byte[] getEntryPrefix() { return EMPTY_PREFIX; }
  2485. @Override
  2486. public void setEntryPrefix(byte[] entryPrefix) { }
  2487. @Override
  2488. public List<Entry> getEntries() { return Collections.emptyList(); }
  2489. @Override
  2490. public void setEntries(List<Entry> entries) { }
  2491. @Override
  2492. public void addEntry(int idx, Entry entry) { }
  2493. @Override
  2494. public Entry removeEntry(int idx) { return null; }
  2495. }
  2496. /**
  2497. * Utility class which maintains information about a pending index update.
  2498. * An instance of this class can be used to complete the change (by calling
  2499. * {@link #commit}) or undo the change (by calling {@link #rollback}).
  2500. */
  2501. public static abstract class PendingChange
  2502. {
  2503. private final PendingChange _next;
  2504. private PendingChange(PendingChange next) {
  2505. _next = next;
  2506. }
  2507. /**
  2508. * Returns the next pending change, if any
  2509. */
  2510. public PendingChange getNext() {
  2511. return _next;
  2512. }
  2513. /**
  2514. * Completes the pending change.
  2515. */
  2516. public abstract void commit() throws IOException;
  2517. /**
  2518. * Undoes the pending change.
  2519. */
  2520. public abstract void rollback() throws IOException;
  2521. }
  2522. /**
  2523. * PendingChange for a row addition.
  2524. */
  2525. private class AddRowPendingChange extends PendingChange
  2526. {
  2527. protected Entry _addEntry;
  2528. protected DataPage _addDataPage;
  2529. protected int _addIdx;
  2530. protected boolean _isDupe;
  2531. protected Entry _oldEntry;
  2532. private AddRowPendingChange(PendingChange next) {
  2533. super(next);
  2534. }
  2535. public void setAddRow(Entry addEntry, DataPage dataPage, int idx,
  2536. boolean isDupe) {
  2537. _addEntry = addEntry;
  2538. _addDataPage = dataPage;
  2539. _addIdx = idx;
  2540. _isDupe = isDupe;
  2541. }
  2542. public void setOldRow(Entry oldEntry) {
  2543. _oldEntry = oldEntry;
  2544. }
  2545. @Override
  2546. public void commit() throws IOException {
  2547. commitAddRow(_addEntry, _addDataPage, _addIdx, _isDupe, _oldEntry);
  2548. }
  2549. @Override
  2550. public void rollback() throws IOException {
  2551. _addEntry = null;
  2552. _addDataPage = null;
  2553. _addIdx = -1;
  2554. }
  2555. }
  2556. /**
  2557. * PendingChange for a row update (which is essentially a deletion followed
  2558. * by an addition).
  2559. */
  2560. private class UpdateRowPendingChange extends AddRowPendingChange
  2561. {
  2562. private UpdateRowPendingChange(PendingChange next) {
  2563. super(next);
  2564. }
  2565. @Override
  2566. public void rollback() throws IOException {
  2567. super.rollback();
  2568. rollbackDeletedRow(_oldEntry);
  2569. }
  2570. }
  2571. }