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 83KB

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