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

IndexData.java 86KB

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