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

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