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.

Table.java 85KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645
  1. /*
  2. Copyright (c) 2005 Health Market Science, Inc.
  3. This library is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU Lesser General Public
  5. License as published by the Free Software Foundation; either
  6. version 2.1 of the License, or (at your option) any later version.
  7. This library is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  10. Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public
  12. License along with this library; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  14. USA
  15. You can contact Health Market Science at info@healthmarketscience.com
  16. or at the following address:
  17. Health Market Science
  18. 2700 Horizon Drive
  19. Suite 200
  20. King of Prussia, PA 19406
  21. */
  22. package com.healthmarketscience.jackcess;
  23. import java.io.IOException;
  24. import java.nio.ByteBuffer;
  25. import java.nio.charset.Charset;
  26. import java.util.ArrayList;
  27. import java.util.Arrays;
  28. import java.util.Collection;
  29. import java.util.Collections;
  30. import java.util.Comparator;
  31. import java.util.HashMap;
  32. import java.util.Iterator;
  33. import java.util.LinkedHashMap;
  34. import java.util.LinkedHashSet;
  35. import java.util.List;
  36. import java.util.Map;
  37. import java.util.Set;
  38. import org.apache.commons.logging.Log;
  39. import org.apache.commons.logging.LogFactory;
  40. /**
  41. * A single database table
  42. * <p>
  43. * Is not thread-safe.
  44. *
  45. * @author Tim McCune
  46. * @usage _general_class_
  47. */
  48. public class Table
  49. implements Iterable<Map<String, Object>>
  50. {
  51. private static final Log LOG = LogFactory.getLog(Table.class);
  52. private static final short OFFSET_MASK = (short)0x1FFF;
  53. private static final short DELETED_ROW_MASK = (short)0x8000;
  54. private static final short OVERFLOW_ROW_MASK = (short)0x4000;
  55. static final int MAGIC_TABLE_NUMBER = 1625;
  56. private static final int MAX_BYTE = 256;
  57. /**
  58. * Table type code for system tables
  59. * @usage _intermediate_class_
  60. */
  61. public static final byte TYPE_SYSTEM = 0x53;
  62. /**
  63. * Table type code for user tables
  64. * @usage _intermediate_class_
  65. */
  66. public static final byte TYPE_USER = 0x4e;
  67. /**
  68. * enum which controls the ordering of the columns in a table.
  69. * @usage _intermediate_class_
  70. */
  71. public enum ColumnOrder {
  72. /** columns are ordered based on the order of the data in the table (this
  73. order does not change as columns are added to the table). */
  74. DATA,
  75. /** columns are ordered based on the "display" order (this order can be
  76. changed arbitrarily) */
  77. DISPLAY;
  78. }
  79. /** comparator which sorts variable length columns based on their index into
  80. the variable length offset table */
  81. private static final Comparator<Column> VAR_LEN_COLUMN_COMPARATOR =
  82. new Comparator<Column>() {
  83. public int compare(Column c1, Column c2) {
  84. return ((c1.getVarLenTableIndex() < c2.getVarLenTableIndex()) ? -1 :
  85. ((c1.getVarLenTableIndex() > c2.getVarLenTableIndex()) ? 1 :
  86. 0));
  87. }
  88. };
  89. /** comparator which sorts columns based on their display index */
  90. private static final Comparator<Column> DISPLAY_ORDER_COMPARATOR =
  91. new Comparator<Column>() {
  92. public int compare(Column c1, Column c2) {
  93. return ((c1.getDisplayIndex() < c2.getDisplayIndex()) ? -1 :
  94. ((c1.getDisplayIndex() > c2.getDisplayIndex()) ? 1 :
  95. 0));
  96. }
  97. };
  98. /** owning database */
  99. private final Database _database;
  100. /** additional table flags from the catalog entry */
  101. private int _flags;
  102. /** Type of the table (either TYPE_SYSTEM or TYPE_USER) */
  103. private byte _tableType;
  104. /** Number of actual indexes on the table */
  105. private int _indexCount;
  106. /** Number of logical indexes for the table */
  107. private int _logicalIndexCount;
  108. /** Number of rows in the table */
  109. private int _rowCount;
  110. /** last long auto number for the table */
  111. private int _lastLongAutoNumber;
  112. /** last complex type auto number for the table */
  113. private int _lastComplexTypeAutoNumber;
  114. /** page number of the definition of this table */
  115. private final int _tableDefPageNumber;
  116. /** max Number of columns in the table (includes previous deletions) */
  117. private short _maxColumnCount;
  118. /** max Number of variable columns in the table */
  119. private short _maxVarColumnCount;
  120. /** List of columns in this table, ordered by column number */
  121. private List<Column> _columns = new ArrayList<Column>();
  122. /** List of variable length columns in this table, ordered by offset */
  123. private final List<Column> _varColumns = new ArrayList<Column>();
  124. /** List of autonumber columns in this table, ordered by column number */
  125. private List<Column> _autoNumColumns;
  126. /** List of indexes on this table (multiple logical indexes may be backed by
  127. the same index data) */
  128. private final List<Index> _indexes = new ArrayList<Index>();
  129. /** List of index datas on this table (the actual backing data for an
  130. index) */
  131. private final List<IndexData> _indexDatas = new ArrayList<IndexData>();
  132. /** List of columns in this table which are in one or more indexes */
  133. private final Set<Column> _indexColumns = new LinkedHashSet<Column>();
  134. /** Table name as stored in Database */
  135. private final String _name;
  136. /** Usage map of pages that this table owns */
  137. private UsageMap _ownedPages;
  138. /** Usage map of pages that this table owns with free space on them */
  139. private UsageMap _freeSpacePages;
  140. /** modification count for the table, keeps row-states up-to-date */
  141. private int _modCount;
  142. /** page buffer used to update data pages when adding rows */
  143. private final TempPageHolder _addRowBufferH =
  144. TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
  145. /** page buffer used to update the table def page */
  146. private final TempPageHolder _tableDefBufferH =
  147. TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
  148. /** buffer used to writing single rows of data */
  149. private final TempBufferHolder _singleRowBufferH =
  150. TempBufferHolder.newHolder(TempBufferHolder.Type.SOFT, true);
  151. /** "buffer" used to writing multi rows of data (will create new buffer on
  152. every call) */
  153. private final TempBufferHolder _multiRowBufferH =
  154. TempBufferHolder.newHolder(TempBufferHolder.Type.NONE, true);
  155. /** page buffer used to write out-of-row "long value" data */
  156. private final TempPageHolder _longValueBufferH =
  157. TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
  158. /** "big index support" is optional */
  159. private final boolean _useBigIndex;
  160. /** optional error handler to use when row errors are encountered */
  161. private ErrorHandler _tableErrorHandler;
  162. /** properties for this table */
  163. private PropertyMap _props;
  164. /** properties group for this table (and columns) */
  165. private PropertyMaps _propertyMaps;
  166. /** foreign-key enforcer for this table */
  167. private final FKEnforcer _fkEnforcer;
  168. /** common cursor for iterating through the table, kept here for historic
  169. reasons */
  170. private Cursor _cursor;
  171. /**
  172. * Only used by unit tests
  173. */
  174. Table(boolean testing, List<Column> columns) throws IOException {
  175. if(!testing) {
  176. throw new IllegalArgumentException();
  177. }
  178. _database = null;
  179. _tableDefPageNumber = PageChannel.INVALID_PAGE_NUMBER;
  180. _name = null;
  181. _useBigIndex = true;
  182. setColumns(columns);
  183. _fkEnforcer = null;
  184. }
  185. /**
  186. * @param database database which owns this table
  187. * @param tableBuffer Buffer to read the table with
  188. * @param pageNumber Page number of the table definition
  189. * @param name Table name
  190. * @param useBigIndex whether or not "big index support" should be enabled
  191. * for the table
  192. */
  193. protected Table(Database database, ByteBuffer tableBuffer,
  194. int pageNumber, String name, int flags, boolean useBigIndex)
  195. throws IOException
  196. {
  197. _database = database;
  198. _tableDefPageNumber = pageNumber;
  199. _name = name;
  200. _flags = flags;
  201. _useBigIndex = useBigIndex;
  202. readTableDefinition(loadCompleteTableDefinitionBuffer(tableBuffer));
  203. _fkEnforcer = new FKEnforcer(this);
  204. }
  205. /**
  206. * @return The name of the table
  207. * @usage _general_method_
  208. */
  209. public String getName() {
  210. return _name;
  211. }
  212. /**
  213. * Whether or not this table has been marked as hidden.
  214. * @usage _general_method_
  215. */
  216. public boolean isHidden() {
  217. return((_flags & Database.HIDDEN_OBJECT_FLAG) != 0);
  218. }
  219. /**
  220. * @usage _advanced_method_
  221. */
  222. public boolean doUseBigIndex() {
  223. return _useBigIndex;
  224. }
  225. /**
  226. * @usage _advanced_method_
  227. */
  228. public int getMaxColumnCount() {
  229. return _maxColumnCount;
  230. }
  231. /**
  232. * @usage _general_method_
  233. */
  234. public int getColumnCount() {
  235. return _columns.size();
  236. }
  237. /**
  238. * @usage _general_method_
  239. */
  240. public Database getDatabase() {
  241. return _database;
  242. }
  243. /**
  244. * @usage _advanced_method_
  245. */
  246. public JetFormat getFormat() {
  247. return getDatabase().getFormat();
  248. }
  249. /**
  250. * @usage _advanced_method_
  251. */
  252. public PageChannel getPageChannel() {
  253. return getDatabase().getPageChannel();
  254. }
  255. /**
  256. * Gets the currently configured ErrorHandler (always non-{@code null}).
  257. * This will be used to handle all errors unless overridden at the Cursor
  258. * level.
  259. * @usage _intermediate_method_
  260. */
  261. public ErrorHandler getErrorHandler() {
  262. return((_tableErrorHandler != null) ? _tableErrorHandler :
  263. getDatabase().getErrorHandler());
  264. }
  265. /**
  266. * Sets a new ErrorHandler. If {@code null}, resets to using the
  267. * ErrorHandler configured at the Database level.
  268. * @usage _intermediate_method_
  269. */
  270. public void setErrorHandler(ErrorHandler newErrorHandler) {
  271. _tableErrorHandler = newErrorHandler;
  272. }
  273. public int getTableDefPageNumber() {
  274. return _tableDefPageNumber;
  275. }
  276. /**
  277. * @usage _advanced_method_
  278. */
  279. public RowState createRowState() {
  280. return new RowState(TempBufferHolder.Type.HARD);
  281. }
  282. protected UsageMap.PageCursor getOwnedPagesCursor() {
  283. return _ownedPages.cursor();
  284. }
  285. /**
  286. * Returns the <i>approximate</i> number of database pages owned by this
  287. * table and all related indexes (this number does <i>not</i> take into
  288. * account pages used for large OLE/MEMO fields).
  289. * <p>
  290. * To calculate the approximate number of bytes owned by a table:
  291. * <code>
  292. * int approxTableBytes = (table.getApproximateOwnedPageCount() *
  293. * table.getFormat().PAGE_SIZE);
  294. * </code>
  295. * @usage _intermediate_method_
  296. */
  297. public int getApproximateOwnedPageCount() {
  298. // add a page for the table def (although that might actually be more than
  299. // one page)
  300. int count = _ownedPages.getPageCount() + 1;
  301. for(Column col : _columns) {
  302. count += col.getOwnedPageCount();
  303. }
  304. // note, we count owned pages from _physical_ indexes, not logical indexes
  305. // (otherwise we could double count pages)
  306. for(IndexData indexData : _indexDatas) {
  307. count += indexData.getOwnedPageCount();
  308. }
  309. return count;
  310. }
  311. protected TempPageHolder getLongValueBuffer() {
  312. return _longValueBufferH;
  313. }
  314. /**
  315. * @return All of the columns in this table (unmodifiable List)
  316. * @usage _general_method_
  317. */
  318. public List<Column> getColumns() {
  319. return Collections.unmodifiableList(_columns);
  320. }
  321. /**
  322. * @return the column with the given name
  323. * @usage _general_method_
  324. */
  325. public Column getColumn(String name) {
  326. for(Column column : _columns) {
  327. if(column.getName().equalsIgnoreCase(name)) {
  328. return column;
  329. }
  330. }
  331. throw new IllegalArgumentException("Column with name " + name +
  332. " does not exist in this table");
  333. }
  334. /**
  335. * Only called by unit tests
  336. */
  337. private void setColumns(List<Column> columns) {
  338. _columns = columns;
  339. int colIdx = 0;
  340. int varLenIdx = 0;
  341. int fixedOffset = 0;
  342. for(Column col : _columns) {
  343. col.setColumnNumber((short)colIdx);
  344. col.setColumnIndex(colIdx++);
  345. if(col.isVariableLength()) {
  346. col.setVarLenTableIndex(varLenIdx++);
  347. _varColumns.add(col);
  348. } else {
  349. col.setFixedDataOffset(fixedOffset);
  350. fixedOffset += col.getType().getFixedSize();
  351. }
  352. }
  353. _maxColumnCount = (short)_columns.size();
  354. _maxVarColumnCount = (short)_varColumns.size();
  355. _autoNumColumns = getAutoNumberColumns(columns);
  356. }
  357. /**
  358. * @return the properties for this table
  359. * @usage _general_method_
  360. */
  361. public PropertyMap getProperties() throws IOException {
  362. if(_props == null) {
  363. _props = getPropertyMaps().getDefault();
  364. }
  365. return _props;
  366. }
  367. /**
  368. * @return all PropertyMaps for this table (and columns)
  369. * @usage _general_method_
  370. */
  371. protected PropertyMaps getPropertyMaps() throws IOException {
  372. if(_propertyMaps == null) {
  373. _propertyMaps = getDatabase().getPropertiesForObject(
  374. _tableDefPageNumber);
  375. }
  376. return _propertyMaps;
  377. }
  378. /**
  379. * @return All of the Indexes on this table (unmodifiable List)
  380. * @usage _intermediate_method_
  381. */
  382. public List<Index> getIndexes() {
  383. return Collections.unmodifiableList(_indexes);
  384. }
  385. /**
  386. * @return the index with the given name
  387. * @throws IllegalArgumentException if there is no index with the given name
  388. * @usage _intermediate_method_
  389. */
  390. public Index getIndex(String name) {
  391. for(Index index : _indexes) {
  392. if(index.getName().equalsIgnoreCase(name)) {
  393. return index;
  394. }
  395. }
  396. throw new IllegalArgumentException("Index with name " + name +
  397. " does not exist on this table");
  398. }
  399. /**
  400. * @return the primary key index for this table
  401. * @throws IllegalArgumentException if there is no primary key index on this
  402. * table
  403. * @usage _intermediate_method_
  404. */
  405. public Index getPrimaryKeyIndex() {
  406. for(Index index : _indexes) {
  407. if(index.isPrimaryKey()) {
  408. return index;
  409. }
  410. }
  411. throw new IllegalArgumentException("Table " + getName() +
  412. " does not have a primary key index");
  413. }
  414. /**
  415. * @return the foreign key index joining this table to the given other table
  416. * @throws IllegalArgumentException if there is no relationship between this
  417. * table and the given table
  418. * @usage _intermediate_method_
  419. */
  420. public Index getForeignKeyIndex(Table otherTable) {
  421. for(Index index : _indexes) {
  422. if(index.isForeignKey() && (index.getReference() != null) &&
  423. (index.getReference().getOtherTablePageNumber() ==
  424. otherTable.getTableDefPageNumber())) {
  425. return index;
  426. }
  427. }
  428. throw new IllegalArgumentException(
  429. "Table " + getName() + " does not have a foreign key reference to " +
  430. otherTable.getName());
  431. }
  432. /**
  433. * @return All of the IndexData on this table (unmodifiable List)
  434. */
  435. List<IndexData> getIndexDatas() {
  436. return Collections.unmodifiableList(_indexDatas);
  437. }
  438. /**
  439. * Only called by unit tests
  440. */
  441. int getLogicalIndexCount() {
  442. return _logicalIndexCount;
  443. }
  444. private Cursor getInternalCursor() {
  445. if(_cursor == null) {
  446. _cursor = Cursor.createCursor(this);
  447. }
  448. return _cursor;
  449. }
  450. /**
  451. * After calling this method, getNextRow will return the first row in the
  452. * table, see {@link Cursor#reset}.
  453. * @usage _general_method_
  454. */
  455. public void reset() {
  456. getInternalCursor().reset();
  457. }
  458. /**
  459. * Delete the current row (retrieved by a call to {@link #getNextRow()}).
  460. * @usage _general_method_
  461. */
  462. public void deleteCurrentRow() throws IOException {
  463. getInternalCursor().deleteCurrentRow();
  464. }
  465. /**
  466. * Delete the row on which the given rowState is currently positioned.
  467. * <p>
  468. * Note, this method is not generally meant to be used directly. You should
  469. * use the {@link #deleteCurrentRow} method or use the Cursor class, which
  470. * allows for more complex table interactions.
  471. * @usage _advanced_method_
  472. */
  473. public void deleteRow(RowState rowState, RowId rowId) throws IOException {
  474. requireValidRowId(rowId);
  475. // ensure that the relevant row state is up-to-date
  476. ByteBuffer rowBuffer = positionAtRowHeader(rowState, rowId);
  477. requireNonDeletedRow(rowState, rowId);
  478. // delete flag always gets set in the "header" row (even if data is on
  479. // overflow row)
  480. int pageNumber = rowState.getHeaderRowId().getPageNumber();
  481. int rowNumber = rowState.getHeaderRowId().getRowNumber();
  482. // attempt to fill in index column values
  483. Object[] rowValues = null;
  484. if(!_indexDatas.isEmpty()) {
  485. // move to row data to get index values
  486. rowBuffer = positionAtRowData(rowState, rowId);
  487. for(Column idxCol : _indexColumns) {
  488. getRowColumn(getFormat(), rowBuffer, idxCol, rowState, null);
  489. }
  490. // use any read rowValues to help update the indexes
  491. rowValues = rowState.getRowValues();
  492. // check foreign keys before proceeding w/ deletion
  493. _fkEnforcer.deleteRow(rowValues);
  494. // move back to the header
  495. rowBuffer = positionAtRowHeader(rowState, rowId);
  496. }
  497. // finally, pull the trigger
  498. int rowIndex = getRowStartOffset(rowNumber, getFormat());
  499. rowBuffer.putShort(rowIndex, (short)(rowBuffer.getShort(rowIndex)
  500. | DELETED_ROW_MASK | OVERFLOW_ROW_MASK));
  501. writeDataPage(rowBuffer, pageNumber);
  502. // update the indexes
  503. for(IndexData indexData : _indexDatas) {
  504. indexData.deleteRow(rowValues, rowId);
  505. }
  506. // make sure table def gets updated
  507. updateTableDefinition(-1);
  508. }
  509. /**
  510. * @return The next row in this table (Column name -> Column value)
  511. * @usage _general_method_
  512. */
  513. public Map<String, Object> getNextRow() throws IOException {
  514. return getNextRow(null);
  515. }
  516. /**
  517. * @param columnNames Only column names in this collection will be returned
  518. * @return The next row in this table (Column name -> Column value)
  519. * @usage _general_method_
  520. */
  521. public Map<String, Object> getNextRow(Collection<String> columnNames)
  522. throws IOException
  523. {
  524. return getInternalCursor().getNextRow(columnNames);
  525. }
  526. /**
  527. * Reads a single column from the given row.
  528. * <p>
  529. * Note, this method is not generally meant to be used directly. Instead
  530. * use the Cursor class, which allows for more complex table interactions,
  531. * e.g. {@link Cursor#getCurrentRowValue}.
  532. * @usage _advanced_method_
  533. */
  534. public Object getRowValue(RowState rowState, RowId rowId, Column column)
  535. throws IOException
  536. {
  537. if(this != column.getTable()) {
  538. throw new IllegalArgumentException(
  539. "Given column " + column + " is not from this table");
  540. }
  541. requireValidRowId(rowId);
  542. // position at correct row
  543. ByteBuffer rowBuffer = positionAtRowData(rowState, rowId);
  544. requireNonDeletedRow(rowState, rowId);
  545. return getRowColumn(getFormat(), rowBuffer, column, rowState, null);
  546. }
  547. /**
  548. * Reads some columns from the given row.
  549. * @param columnNames Only column names in this collection will be returned
  550. * @usage _advanced_method_
  551. */
  552. public Map<String, Object> getRow(
  553. RowState rowState, RowId rowId, Collection<String> columnNames)
  554. throws IOException
  555. {
  556. requireValidRowId(rowId);
  557. // position at correct row
  558. ByteBuffer rowBuffer = positionAtRowData(rowState, rowId);
  559. requireNonDeletedRow(rowState, rowId);
  560. return getRow(getFormat(), rowState, rowBuffer, _columns, columnNames);
  561. }
  562. /**
  563. * Reads the row data from the given row buffer. Leaves limit unchanged.
  564. * Saves parsed row values to the given rowState.
  565. */
  566. private static Map<String, Object> getRow(
  567. JetFormat format,
  568. RowState rowState,
  569. ByteBuffer rowBuffer,
  570. Collection<Column> columns,
  571. Collection<String> columnNames)
  572. throws IOException
  573. {
  574. Map<String, Object> rtn = new LinkedHashMap<String, Object>(
  575. columns.size());
  576. for(Column column : columns) {
  577. if((columnNames == null) || (columnNames.contains(column.getName()))) {
  578. // Add the value to the row data
  579. column.setRowValue(
  580. rtn, getRowColumn(format, rowBuffer, column, rowState, null));
  581. }
  582. }
  583. return rtn;
  584. }
  585. /**
  586. * Reads the column data from the given row buffer. Leaves limit unchanged.
  587. * Caches the returned value in the rowState.
  588. */
  589. private static Object getRowColumn(JetFormat format,
  590. ByteBuffer rowBuffer,
  591. Column column,
  592. RowState rowState,
  593. Map<Column,byte[]> rawVarValues)
  594. throws IOException
  595. {
  596. byte[] columnData = null;
  597. try {
  598. NullMask nullMask = rowState.getNullMask(rowBuffer);
  599. boolean isNull = nullMask.isNull(column);
  600. if(column.getType() == DataType.BOOLEAN) {
  601. // Boolean values are stored in the null mask. see note about
  602. // caching below
  603. return rowState.setRowValue(column.getColumnIndex(),
  604. Boolean.valueOf(!isNull));
  605. } else if(isNull) {
  606. // well, that's easy! (no need to update cache w/ null)
  607. return null;
  608. }
  609. // reset position to row start
  610. rowBuffer.reset();
  611. // locate the column data bytes
  612. int rowStart = rowBuffer.position();
  613. int colDataPos = 0;
  614. int colDataLen = 0;
  615. if(!column.isVariableLength()) {
  616. // read fixed length value (non-boolean at this point)
  617. int dataStart = rowStart + format.OFFSET_COLUMN_FIXED_DATA_ROW_OFFSET;
  618. colDataPos = dataStart + column.getFixedDataOffset();
  619. colDataLen = column.getType().getFixedSize(column.getLength());
  620. } else {
  621. int varDataStart;
  622. int varDataEnd;
  623. if(format.SIZE_ROW_VAR_COL_OFFSET == 2) {
  624. // read simple var length value
  625. int varColumnOffsetPos =
  626. (rowBuffer.limit() - nullMask.byteSize() - 4) -
  627. (column.getVarLenTableIndex() * 2);
  628. varDataStart = rowBuffer.getShort(varColumnOffsetPos);
  629. varDataEnd = rowBuffer.getShort(varColumnOffsetPos - 2);
  630. } else {
  631. // read jump-table based var length values
  632. short[] varColumnOffsets = readJumpTableVarColOffsets(
  633. rowState, rowBuffer, rowStart, nullMask);
  634. varDataStart = varColumnOffsets[column.getVarLenTableIndex()];
  635. varDataEnd = varColumnOffsets[column.getVarLenTableIndex() + 1];
  636. }
  637. colDataPos = rowStart + varDataStart;
  638. colDataLen = varDataEnd - varDataStart;
  639. }
  640. // grab the column data
  641. rowBuffer.position(colDataPos);
  642. columnData = ByteUtil.getBytes(rowBuffer, colDataLen);
  643. if((rawVarValues != null) && column.isVariableLength()) {
  644. // caller wants raw value as well
  645. rawVarValues.put(column, columnData);
  646. }
  647. // parse the column data. we cache the row values in order to be able
  648. // to update the index on row deletion. note, most of the returned
  649. // values are immutable, except for binary data (returned as byte[]),
  650. // but binary data shouldn't be indexed anyway.
  651. return rowState.setRowValue(column.getColumnIndex(),
  652. column.read(columnData));
  653. } catch(Exception e) {
  654. // cache "raw" row value. see note about caching above
  655. rowState.setRowValue(column.getColumnIndex(),
  656. Column.rawDataWrapper(columnData));
  657. return rowState.handleRowError(column, columnData, e);
  658. }
  659. }
  660. private static short[] readJumpTableVarColOffsets(
  661. RowState rowState, ByteBuffer rowBuffer, int rowStart,
  662. NullMask nullMask)
  663. {
  664. short[] varColOffsets = rowState.getVarColOffsets();
  665. if(varColOffsets != null) {
  666. return varColOffsets;
  667. }
  668. // calculate offsets using jump-table info
  669. int nullMaskSize = nullMask.byteSize();
  670. int rowEnd = rowStart + rowBuffer.remaining() - 1;
  671. int numVarCols = ByteUtil.getUnsignedByte(rowBuffer,
  672. rowEnd - nullMaskSize);
  673. varColOffsets = new short[numVarCols + 1];
  674. int rowLen = rowEnd - rowStart + 1;
  675. int numJumps = (rowLen - 1) / MAX_BYTE;
  676. int colOffset = rowEnd - nullMaskSize - numJumps - 1;
  677. // If last jump is a dummy value, ignore it
  678. if(((colOffset - rowStart - numVarCols) / MAX_BYTE) < numJumps) {
  679. numJumps--;
  680. }
  681. int jumpsUsed = 0;
  682. for(int i = 0; i < numVarCols + 1; i++) {
  683. while((jumpsUsed < numJumps) &&
  684. (i == ByteUtil.getUnsignedByte(
  685. rowBuffer, rowEnd - nullMaskSize-jumpsUsed - 1))) {
  686. jumpsUsed++;
  687. }
  688. varColOffsets[i] = (short)
  689. (ByteUtil.getUnsignedByte(rowBuffer, colOffset - i)
  690. + (jumpsUsed * MAX_BYTE));
  691. }
  692. rowState.setVarColOffsets(varColOffsets);
  693. return varColOffsets;
  694. }
  695. /**
  696. * Reads the null mask from the given row buffer. Leaves limit unchanged.
  697. */
  698. private NullMask getRowNullMask(ByteBuffer rowBuffer)
  699. throws IOException
  700. {
  701. // reset position to row start
  702. rowBuffer.reset();
  703. // Number of columns in this row
  704. int columnCount = ByteUtil.getUnsignedVarInt(
  705. rowBuffer, getFormat().SIZE_ROW_COLUMN_COUNT);
  706. // read null mask
  707. NullMask nullMask = new NullMask(columnCount);
  708. rowBuffer.position(rowBuffer.limit() - nullMask.byteSize()); //Null mask at end
  709. nullMask.read(rowBuffer);
  710. return nullMask;
  711. }
  712. /**
  713. * Sets a new buffer to the correct row header page using the given rowState
  714. * according to the given rowId. Deleted state is
  715. * determined, but overflow row pointers are not followed.
  716. *
  717. * @return a ByteBuffer of the relevant page, or null if row was invalid
  718. * @usage _advanced_method_
  719. */
  720. public static ByteBuffer positionAtRowHeader(RowState rowState, RowId rowId)
  721. throws IOException
  722. {
  723. ByteBuffer rowBuffer = rowState.setHeaderRow(rowId);
  724. if(rowState.isAtHeaderRow()) {
  725. // this task has already been accomplished
  726. return rowBuffer;
  727. }
  728. if(!rowState.isValid()) {
  729. // this was an invalid page/row
  730. rowState.setStatus(RowStateStatus.AT_HEADER);
  731. return null;
  732. }
  733. // note, we don't use findRowStart here cause we need the unmasked value
  734. short rowStart = rowBuffer.getShort(
  735. getRowStartOffset(rowId.getRowNumber(),
  736. rowState.getTable().getFormat()));
  737. // check the deleted, overflow flags for the row (the "real" flags are
  738. // always set on the header row)
  739. RowStatus rowStatus = RowStatus.NORMAL;
  740. if(isDeletedRow(rowStart)) {
  741. rowStatus = RowStatus.DELETED;
  742. } else if(isOverflowRow(rowStart)) {
  743. rowStatus = RowStatus.OVERFLOW;
  744. }
  745. rowState.setRowStatus(rowStatus);
  746. rowState.setStatus(RowStateStatus.AT_HEADER);
  747. return rowBuffer;
  748. }
  749. /**
  750. * Sets the position and limit in a new buffer using the given rowState
  751. * according to the given row number and row end, following overflow row
  752. * pointers as necessary.
  753. *
  754. * @return a ByteBuffer narrowed to the actual row data, or null if row was
  755. * invalid or deleted
  756. * @usage _advanced_method_
  757. */
  758. public static ByteBuffer positionAtRowData(RowState rowState, RowId rowId)
  759. throws IOException
  760. {
  761. positionAtRowHeader(rowState, rowId);
  762. if(!rowState.isValid() || rowState.isDeleted()) {
  763. // row is invalid or deleted
  764. rowState.setStatus(RowStateStatus.AT_FINAL);
  765. return null;
  766. }
  767. ByteBuffer rowBuffer = rowState.getFinalPage();
  768. int rowNum = rowState.getFinalRowId().getRowNumber();
  769. JetFormat format = rowState.getTable().getFormat();
  770. if(rowState.isAtFinalRow()) {
  771. // we've already found the final row data
  772. return PageChannel.narrowBuffer(
  773. rowBuffer,
  774. findRowStart(rowBuffer, rowNum, format),
  775. findRowEnd(rowBuffer, rowNum, format));
  776. }
  777. while(true) {
  778. // note, we don't use findRowStart here cause we need the unmasked value
  779. short rowStart = rowBuffer.getShort(getRowStartOffset(rowNum, format));
  780. short rowEnd = findRowEnd(rowBuffer, rowNum, format);
  781. // note, at this point we know the row is not deleted, so ignore any
  782. // subsequent deleted flags (as overflow rows are always marked deleted
  783. // anyway)
  784. boolean overflowRow = isOverflowRow(rowStart);
  785. // now, strip flags from rowStart offset
  786. rowStart = (short)(rowStart & OFFSET_MASK);
  787. if (overflowRow) {
  788. if((rowEnd - rowStart) < 4) {
  789. throw new IOException("invalid overflow row info");
  790. }
  791. // Overflow page. the "row" data in the current page points to
  792. // another page/row
  793. int overflowRowNum = ByteUtil.getUnsignedByte(rowBuffer, rowStart);
  794. int overflowPageNum = ByteUtil.get3ByteInt(rowBuffer, rowStart + 1);
  795. rowBuffer = rowState.setOverflowRow(
  796. new RowId(overflowPageNum, overflowRowNum));
  797. rowNum = overflowRowNum;
  798. } else {
  799. rowState.setStatus(RowStateStatus.AT_FINAL);
  800. return PageChannel.narrowBuffer(rowBuffer, rowStart, rowEnd);
  801. }
  802. }
  803. }
  804. /**
  805. * Calls <code>reset</code> on this table and returns a modifiable
  806. * Iterator which will iterate through all the rows of this table. Use of
  807. * the Iterator follows the same restrictions as a call to
  808. * <code>getNextRow</code>.
  809. * @throws IllegalStateException if an IOException is thrown by one of the
  810. * operations, the actual exception will be contained within
  811. * @usage _general_method_
  812. */
  813. public Iterator<Map<String, Object>> iterator()
  814. {
  815. return iterator(null);
  816. }
  817. /**
  818. * Calls <code>reset</code> on this table and returns a modifiable
  819. * Iterator which will iterate through all the rows of this table, returning
  820. * only the given columns. Use of the Iterator follows the same
  821. * restrictions as a call to <code>getNextRow</code>.
  822. * @throws IllegalStateException if an IOException is thrown by one of the
  823. * operations, the actual exception will be contained within
  824. * @usage _general_method_
  825. */
  826. public Iterator<Map<String, Object>> iterator(Collection<String> columnNames)
  827. {
  828. reset();
  829. return getInternalCursor().iterator(columnNames);
  830. }
  831. /**
  832. * Writes a new table defined by the given TableCreator to the database.
  833. * @usage _advanced_method_
  834. */
  835. protected static void writeTableDefinition(TableCreator creator)
  836. throws IOException
  837. {
  838. // first, create the usage map page
  839. createUsageMapDefinitionBuffer(creator);
  840. // next, determine how big the table def will be (in case it will be more
  841. // than one page)
  842. JetFormat format = creator.getFormat();
  843. int idxDataLen = (creator.getIndexCount() *
  844. (format.SIZE_INDEX_DEFINITION +
  845. format.SIZE_INDEX_COLUMN_BLOCK)) +
  846. (creator.getLogicalIndexCount() * format.SIZE_INDEX_INFO_BLOCK);
  847. int colUmapLen = creator.getLongValueColumns().size() * 10;
  848. int totalTableDefSize = format.SIZE_TDEF_HEADER +
  849. (format.SIZE_COLUMN_DEF_BLOCK * creator.getColumns().size()) +
  850. idxDataLen + colUmapLen + format.SIZE_TDEF_TRAILER;
  851. // total up the amount of space used by the column and index names (2
  852. // bytes per char + 2 bytes for the length)
  853. for(Column col : creator.getColumns()) {
  854. int nameByteLen = (col.getName().length() *
  855. JetFormat.TEXT_FIELD_UNIT_SIZE);
  856. totalTableDefSize += nameByteLen + 2;
  857. }
  858. for(IndexBuilder idx : creator.getIndexes()) {
  859. int nameByteLen = (idx.getName().length() *
  860. JetFormat.TEXT_FIELD_UNIT_SIZE);
  861. totalTableDefSize += nameByteLen + 2;
  862. }
  863. // now, create the table definition
  864. PageChannel pageChannel = creator.getPageChannel();
  865. ByteBuffer buffer = pageChannel .createBuffer(Math.max(totalTableDefSize,
  866. format.PAGE_SIZE));
  867. writeTableDefinitionHeader(creator, buffer, totalTableDefSize);
  868. if(creator.hasIndexes()) {
  869. // index row counts
  870. IndexData.writeRowCountDefinitions(creator, buffer);
  871. }
  872. // column definitions
  873. Column.writeDefinitions(creator, buffer);
  874. if(creator.hasIndexes()) {
  875. // index and index data definitions
  876. IndexData.writeDefinitions(creator, buffer);
  877. Index.writeDefinitions(creator, buffer);
  878. }
  879. // write long value column usage map references
  880. for(Column lvalCol : creator.getLongValueColumns()) {
  881. buffer.putShort(lvalCol.getColumnNumber());
  882. TableCreator.ColumnState colState =
  883. creator.getColumnState(lvalCol);
  884. // owned pages umap (both are on same page)
  885. buffer.put(colState.getUmapOwnedRowNumber());
  886. ByteUtil.put3ByteInt(buffer, colState.getUmapPageNumber());
  887. // free space pages umap
  888. buffer.put(colState.getUmapFreeRowNumber());
  889. ByteUtil.put3ByteInt(buffer, colState.getUmapPageNumber());
  890. }
  891. //End of tabledef
  892. buffer.put((byte) 0xff);
  893. buffer.put((byte) 0xff);
  894. // write table buffer to database
  895. if(totalTableDefSize <= format.PAGE_SIZE) {
  896. // easy case, fits on one page
  897. buffer.putShort(format.OFFSET_FREE_SPACE,
  898. (short)(buffer.remaining() - 8)); // overwrite page free space
  899. // Write the tdef page to disk.
  900. pageChannel.writePage(buffer, creator.getTdefPageNumber());
  901. } else {
  902. // need to split across multiple pages
  903. ByteBuffer partialTdef = pageChannel.createPageBuffer();
  904. buffer.rewind();
  905. int nextTdefPageNumber = PageChannel.INVALID_PAGE_NUMBER;
  906. while(buffer.hasRemaining()) {
  907. // reset for next write
  908. partialTdef.clear();
  909. if(nextTdefPageNumber == PageChannel.INVALID_PAGE_NUMBER) {
  910. // this is the first page. note, the first page already has the
  911. // page header, so no need to write it here
  912. nextTdefPageNumber = creator.getTdefPageNumber();
  913. } else {
  914. // write page header
  915. writeTablePageHeader(partialTdef);
  916. }
  917. // copy the next page of tdef bytes
  918. int curTdefPageNumber = nextTdefPageNumber;
  919. int writeLen = Math.min(partialTdef.remaining(), buffer.remaining());
  920. partialTdef.put(buffer.array(), buffer.position(), writeLen);
  921. ByteUtil.forward(buffer, writeLen);
  922. if(buffer.hasRemaining()) {
  923. // need a next page
  924. nextTdefPageNumber = pageChannel.allocateNewPage();
  925. partialTdef.putInt(format.OFFSET_NEXT_TABLE_DEF_PAGE,
  926. nextTdefPageNumber);
  927. }
  928. // update page free space
  929. partialTdef.putShort(format.OFFSET_FREE_SPACE,
  930. (short)(partialTdef.remaining() - 8)); // overwrite page free space
  931. // write partial page to disk
  932. pageChannel.writePage(partialTdef, curTdefPageNumber);
  933. }
  934. }
  935. }
  936. /**
  937. * @param buffer Buffer to write to
  938. * @param columns List of Columns in the table
  939. */
  940. private static void writeTableDefinitionHeader(
  941. TableCreator creator, ByteBuffer buffer, int totalTableDefSize)
  942. throws IOException
  943. {
  944. List<Column> columns = creator.getColumns();
  945. //Start writing the tdef
  946. writeTablePageHeader(buffer);
  947. buffer.putInt(totalTableDefSize); //Length of table def
  948. buffer.putInt(MAGIC_TABLE_NUMBER); // seemingly constant magic value
  949. buffer.putInt(0); //Number of rows
  950. buffer.putInt(0); //Last Autonumber
  951. buffer.put((byte) 1); // this makes autonumbering work in access
  952. for (int i = 0; i < 15; i++) { //Unknown
  953. buffer.put((byte) 0);
  954. }
  955. buffer.put(Table.TYPE_USER); //Table type
  956. buffer.putShort((short) columns.size()); //Max columns a row will have
  957. buffer.putShort(Column.countVariableLength(columns)); //Number of variable columns in table
  958. buffer.putShort((short) columns.size()); //Number of columns in table
  959. buffer.putInt(creator.getLogicalIndexCount()); //Number of logical indexes in table
  960. buffer.putInt(creator.getIndexCount()); //Number of indexes in table
  961. buffer.put((byte) 0); //Usage map row number
  962. ByteUtil.put3ByteInt(buffer, creator.getUmapPageNumber()); //Usage map page number
  963. buffer.put((byte) 1); //Free map row number
  964. ByteUtil.put3ByteInt(buffer, creator.getUmapPageNumber()); //Free map page number
  965. if (LOG.isDebugEnabled()) {
  966. int position = buffer.position();
  967. buffer.rewind();
  968. LOG.debug("Creating new table def block:\n" + ByteUtil.toHexString(
  969. buffer, creator.getFormat().SIZE_TDEF_HEADER));
  970. buffer.position(position);
  971. }
  972. }
  973. /**
  974. * Writes the page header for a table definition page
  975. * @param buffer Buffer to write to
  976. */
  977. private static void writeTablePageHeader(ByteBuffer buffer)
  978. {
  979. buffer.put(PageTypes.TABLE_DEF); //Page type
  980. buffer.put((byte) 0x01); //Unknown
  981. buffer.put((byte) 0); //Unknown
  982. buffer.put((byte) 0); //Unknown
  983. buffer.putInt(0); //Next TDEF page pointer
  984. }
  985. /**
  986. * Writes the given name into the given buffer in the format as expected by
  987. * {@link #readName}.
  988. */
  989. static void writeName(ByteBuffer buffer, String name, Charset charset)
  990. {
  991. ByteBuffer encName = Column.encodeUncompressedText(name, charset);
  992. buffer.putShort((short) encName.remaining());
  993. buffer.put(encName);
  994. }
  995. /**
  996. * Create the usage map definition page buffer. The "used pages" map is in
  997. * row 0, the "pages with free space" map is in row 1. Index usage maps are
  998. * in subsequent rows.
  999. */
  1000. private static void createUsageMapDefinitionBuffer(TableCreator creator)
  1001. throws IOException
  1002. {
  1003. List<Column> lvalCols = creator.getLongValueColumns();
  1004. // 2 table usage maps plus 1 for each index and 2 for each lval col
  1005. int indexUmapEnd = 2 + creator.getIndexCount();
  1006. int umapNum = indexUmapEnd + (lvalCols.size() * 2);
  1007. JetFormat format = creator.getFormat();
  1008. int umapRowLength = format.OFFSET_USAGE_MAP_START +
  1009. format.USAGE_MAP_TABLE_BYTE_LENGTH;
  1010. int umapSpaceUsage = getRowSpaceUsage(umapRowLength, format);
  1011. PageChannel pageChannel = creator.getPageChannel();
  1012. int umapPageNumber = PageChannel.INVALID_PAGE_NUMBER;
  1013. ByteBuffer umapBuf = null;
  1014. int freeSpace = 0;
  1015. int rowStart = 0;
  1016. int umapRowNum = 0;
  1017. for(int i = 0; i < umapNum; ++i) {
  1018. if(umapBuf == null) {
  1019. // need new page for usage maps
  1020. if(umapPageNumber == PageChannel.INVALID_PAGE_NUMBER) {
  1021. // first umap page has already been reserved
  1022. umapPageNumber = creator.getUmapPageNumber();
  1023. } else {
  1024. // need another umap page
  1025. umapPageNumber = creator.reservePageNumber();
  1026. }
  1027. freeSpace = format.DATA_PAGE_INITIAL_FREE_SPACE;
  1028. umapBuf = pageChannel.createPageBuffer();
  1029. umapBuf.put(PageTypes.DATA);
  1030. umapBuf.put((byte) 0x1); //Unknown
  1031. umapBuf.putShort((short)freeSpace); //Free space in page
  1032. umapBuf.putInt(0); //Table definition
  1033. umapBuf.putInt(0); //Unknown
  1034. umapBuf.putShort((short)0); //Number of records on this page
  1035. rowStart = findRowEnd(umapBuf, 0, format) - umapRowLength;
  1036. umapRowNum = 0;
  1037. }
  1038. umapBuf.putShort(getRowStartOffset(umapRowNum, format), (short)rowStart);
  1039. if(i == 0) {
  1040. // table "owned pages" map definition
  1041. umapBuf.put(rowStart, UsageMap.MAP_TYPE_REFERENCE);
  1042. } else if(i == 1) {
  1043. // table "free space pages" map definition
  1044. umapBuf.put(rowStart, UsageMap.MAP_TYPE_INLINE);
  1045. } else if(i < indexUmapEnd) {
  1046. // index umap
  1047. int indexIdx = i - 2;
  1048. IndexBuilder idx = creator.getIndexes().get(indexIdx);
  1049. // allocate root page for the index
  1050. int rootPageNumber = pageChannel.allocateNewPage();
  1051. // stash info for later use
  1052. TableCreator.IndexState idxState = creator.getIndexState(idx);
  1053. idxState.setRootPageNumber(rootPageNumber);
  1054. idxState.setUmapRowNumber((byte)umapRowNum);
  1055. idxState.setUmapPageNumber(umapPageNumber);
  1056. // index map definition, including initial root page
  1057. umapBuf.put(rowStart, UsageMap.MAP_TYPE_INLINE);
  1058. umapBuf.putInt(rowStart + 1, rootPageNumber);
  1059. umapBuf.put(rowStart + 5, (byte)1);
  1060. } else {
  1061. // long value column umaps
  1062. int lvalColIdx = i - indexUmapEnd;
  1063. int umapType = lvalColIdx % 2;
  1064. lvalColIdx /= 2;
  1065. Column lvalCol = lvalCols.get(lvalColIdx);
  1066. TableCreator.ColumnState colState =
  1067. creator.getColumnState(lvalCol);
  1068. umapBuf.put(rowStart, UsageMap.MAP_TYPE_INLINE);
  1069. if((umapType == 1) &&
  1070. (umapPageNumber != colState.getUmapPageNumber())) {
  1071. // we want to force both usage maps for a column to be on the same
  1072. // data page, so just discard the previous one we wrote
  1073. --i;
  1074. umapType = 0;
  1075. }
  1076. if(umapType == 0) {
  1077. // lval column "owned pages" usage map
  1078. colState.setUmapOwnedRowNumber((byte)umapRowNum);
  1079. colState.setUmapPageNumber(umapPageNumber);
  1080. } else {
  1081. // lval column "free space pages" usage map (always on same page)
  1082. colState.setUmapFreeRowNumber((byte)umapRowNum);
  1083. }
  1084. }
  1085. rowStart -= umapRowLength;
  1086. freeSpace -= umapSpaceUsage;
  1087. ++umapRowNum;
  1088. if((freeSpace <= umapSpaceUsage) || (i == (umapNum - 1))) {
  1089. // finish current page
  1090. umapBuf.putShort(format.OFFSET_FREE_SPACE, (short)freeSpace);
  1091. umapBuf.putShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE,
  1092. (short)umapRowNum);
  1093. pageChannel.writePage(umapBuf, umapPageNumber);
  1094. umapBuf = null;
  1095. }
  1096. }
  1097. }
  1098. /**
  1099. * Returns a single ByteBuffer which contains the entire table definition
  1100. * (which may span multiple database pages).
  1101. */
  1102. private ByteBuffer loadCompleteTableDefinitionBuffer(ByteBuffer tableBuffer)
  1103. throws IOException
  1104. {
  1105. int nextPage = tableBuffer.getInt(getFormat().OFFSET_NEXT_TABLE_DEF_PAGE);
  1106. ByteBuffer nextPageBuffer = null;
  1107. while (nextPage != 0) {
  1108. if (nextPageBuffer == null) {
  1109. nextPageBuffer = getPageChannel().createPageBuffer();
  1110. }
  1111. getPageChannel().readPage(nextPageBuffer, nextPage);
  1112. nextPage = nextPageBuffer.getInt(getFormat().OFFSET_NEXT_TABLE_DEF_PAGE);
  1113. ByteBuffer newBuffer = getPageChannel().createBuffer(
  1114. tableBuffer.capacity() + getFormat().PAGE_SIZE - 8);
  1115. newBuffer.put(tableBuffer);
  1116. newBuffer.put(nextPageBuffer.array(), 8, getFormat().PAGE_SIZE - 8);
  1117. tableBuffer = newBuffer;
  1118. tableBuffer.flip();
  1119. }
  1120. return tableBuffer;
  1121. }
  1122. /**
  1123. * Read the table definition
  1124. */
  1125. private void readTableDefinition(ByteBuffer tableBuffer) throws IOException
  1126. {
  1127. if (LOG.isDebugEnabled()) {
  1128. tableBuffer.rewind();
  1129. LOG.debug("Table def block:\n" + ByteUtil.toHexString(tableBuffer,
  1130. getFormat().SIZE_TDEF_HEADER));
  1131. }
  1132. _rowCount = tableBuffer.getInt(getFormat().OFFSET_NUM_ROWS);
  1133. _lastLongAutoNumber = tableBuffer.getInt(getFormat().OFFSET_NEXT_AUTO_NUMBER);
  1134. if(getFormat().OFFSET_NEXT_COMPLEX_AUTO_NUMBER >= 0) {
  1135. _lastComplexTypeAutoNumber = tableBuffer.getInt(
  1136. getFormat().OFFSET_NEXT_COMPLEX_AUTO_NUMBER);
  1137. }
  1138. _tableType = tableBuffer.get(getFormat().OFFSET_TABLE_TYPE);
  1139. _maxColumnCount = tableBuffer.getShort(getFormat().OFFSET_MAX_COLS);
  1140. _maxVarColumnCount = tableBuffer.getShort(getFormat().OFFSET_NUM_VAR_COLS);
  1141. short columnCount = tableBuffer.getShort(getFormat().OFFSET_NUM_COLS);
  1142. _logicalIndexCount = tableBuffer.getInt(getFormat().OFFSET_NUM_INDEX_SLOTS);
  1143. _indexCount = tableBuffer.getInt(getFormat().OFFSET_NUM_INDEXES);
  1144. tableBuffer.position(getFormat().OFFSET_OWNED_PAGES);
  1145. _ownedPages = UsageMap.read(getDatabase(), tableBuffer, false);
  1146. tableBuffer.position(getFormat().OFFSET_FREE_SPACE_PAGES);
  1147. _freeSpacePages = UsageMap.read(getDatabase(), tableBuffer, false);
  1148. for (int i = 0; i < _indexCount; i++) {
  1149. _indexDatas.add(IndexData.create(this, tableBuffer, i, getFormat()));
  1150. }
  1151. int colOffset = getFormat().OFFSET_INDEX_DEF_BLOCK +
  1152. _indexCount * getFormat().SIZE_INDEX_DEFINITION;
  1153. int dispIndex = 0;
  1154. for (int i = 0; i < columnCount; i++) {
  1155. Column column = new Column(this, tableBuffer,
  1156. colOffset + (i * getFormat().SIZE_COLUMN_HEADER), dispIndex++);
  1157. _columns.add(column);
  1158. if(column.isVariableLength()) {
  1159. // also shove it in the variable columns list, which is ordered
  1160. // differently from the _columns list
  1161. _varColumns.add(column);
  1162. }
  1163. }
  1164. tableBuffer.position(colOffset +
  1165. (columnCount * getFormat().SIZE_COLUMN_HEADER));
  1166. for (int i = 0; i < columnCount; i++) {
  1167. Column column = _columns.get(i);
  1168. column.setName(readName(tableBuffer));
  1169. }
  1170. Collections.sort(_columns);
  1171. _autoNumColumns = getAutoNumberColumns(_columns);
  1172. // setup the data index for the columns
  1173. int colIdx = 0;
  1174. for(Column col : _columns) {
  1175. col.setColumnIndex(colIdx++);
  1176. }
  1177. // sort variable length columns based on their index into the variable
  1178. // length offset table, because we will write the columns in this order
  1179. Collections.sort(_varColumns, VAR_LEN_COLUMN_COMPARATOR);
  1180. // read index column information
  1181. for (int i = 0; i < _indexCount; i++) {
  1182. IndexData idxData = _indexDatas.get(i);
  1183. idxData.read(tableBuffer, _columns);
  1184. // keep track of all columns involved in indexes
  1185. for(IndexData.ColumnDescriptor iCol : idxData.getColumns()) {
  1186. _indexColumns.add(iCol.getColumn());
  1187. }
  1188. }
  1189. // read logical index info (may be more logical indexes than index datas)
  1190. for (int i = 0; i < _logicalIndexCount; i++) {
  1191. _indexes.add(new Index(tableBuffer, _indexDatas, getFormat()));
  1192. }
  1193. // read logical index names
  1194. for (int i = 0; i < _logicalIndexCount; i++) {
  1195. _indexes.get(i).setName(readName(tableBuffer));
  1196. }
  1197. Collections.sort(_indexes);
  1198. // read column usage map info
  1199. while(tableBuffer.remaining() >= 2) {
  1200. short umapColNum = tableBuffer.getShort();
  1201. if(umapColNum == IndexData.COLUMN_UNUSED) {
  1202. break;
  1203. }
  1204. int pos = tableBuffer.position();
  1205. UsageMap colOwnedPages = null;
  1206. UsageMap colFreeSpacePages = null;
  1207. try {
  1208. colOwnedPages = UsageMap.read(getDatabase(), tableBuffer, false);
  1209. colFreeSpacePages = UsageMap.read(getDatabase(), tableBuffer, false);
  1210. } catch(IllegalStateException e) {
  1211. // ignore invalid usage map info
  1212. colOwnedPages = null;
  1213. colFreeSpacePages = null;
  1214. tableBuffer.position(pos + 8);
  1215. LOG.warn("Table " + _name + " invalid column " + umapColNum +
  1216. " usage map definition: " + e);
  1217. }
  1218. for(Column col : _columns) {
  1219. if(col.getColumnNumber() == umapColNum) {
  1220. col.setUsageMaps(colOwnedPages, colFreeSpacePages);
  1221. break;
  1222. }
  1223. }
  1224. }
  1225. // re-sort columns if necessary
  1226. if(getDatabase().getColumnOrder() != ColumnOrder.DATA) {
  1227. Collections.sort(_columns, DISPLAY_ORDER_COMPARATOR);
  1228. }
  1229. for(Column col : _columns) {
  1230. // some columns need to do extra work after the table is completely
  1231. // loaded
  1232. col.postTableLoadInit();
  1233. }
  1234. }
  1235. /**
  1236. * Writes the given page data to the given page number, clears any other
  1237. * relevant buffers.
  1238. */
  1239. private void writeDataPage(ByteBuffer pageBuffer, int pageNumber)
  1240. throws IOException
  1241. {
  1242. // write the page data
  1243. getPageChannel().writePage(pageBuffer, pageNumber);
  1244. // possibly invalidate the add row buffer if a different data buffer is
  1245. // being written (e.g. this happens during deleteRow)
  1246. _addRowBufferH.possiblyInvalidate(pageNumber, pageBuffer);
  1247. // update modification count so any active RowStates can keep themselves
  1248. // up-to-date
  1249. ++_modCount;
  1250. }
  1251. /**
  1252. * Returns a name read from the buffer at the current position. The
  1253. * expected name format is the name length followed by the name
  1254. * encoded using the {@link JetFormat#CHARSET}
  1255. */
  1256. private String readName(ByteBuffer buffer) {
  1257. int nameLength = readNameLength(buffer);
  1258. byte[] nameBytes = ByteUtil.getBytes(buffer, nameLength);
  1259. return Column.decodeUncompressedText(nameBytes,
  1260. getDatabase().getCharset());
  1261. }
  1262. /**
  1263. * Returns a name length read from the buffer at the current position.
  1264. */
  1265. private int readNameLength(ByteBuffer buffer) {
  1266. return ByteUtil.getUnsignedVarInt(buffer, getFormat().SIZE_NAME_LENGTH);
  1267. }
  1268. /**
  1269. * Converts a map of columnName -> columnValue to an array of row values
  1270. * appropriate for a call to {@link #addRow(Object...)}.
  1271. * @usage _general_method_
  1272. */
  1273. public Object[] asRow(Map<String,?> rowMap) {
  1274. return asRow(rowMap, null);
  1275. }
  1276. /**
  1277. * Converts a map of columnName -> columnValue to an array of row values
  1278. * appropriate for a call to {@link #updateCurrentRow(Object...)}.
  1279. * @usage _general_method_
  1280. */
  1281. public Object[] asUpdateRow(Map<String,?> rowMap) {
  1282. return asRow(rowMap, Column.KEEP_VALUE);
  1283. }
  1284. /**
  1285. * Converts a map of columnName -> columnValue to an array of row values.
  1286. */
  1287. private Object[] asRow(Map<String,?> rowMap, Object defaultValue)
  1288. {
  1289. Object[] row = new Object[_columns.size()];
  1290. if(defaultValue != null) {
  1291. Arrays.fill(row, defaultValue);
  1292. }
  1293. if(rowMap == null) {
  1294. return row;
  1295. }
  1296. for(Column col : _columns) {
  1297. if(rowMap.containsKey(col.getName())) {
  1298. col.setRowValue(row, col.getRowValue(rowMap));
  1299. }
  1300. }
  1301. return row;
  1302. }
  1303. /**
  1304. * Adds a single row to this table and writes it to disk. The values are
  1305. * expected to be given in the order that the Columns are listed by the
  1306. * {@link #getColumns} method. This is by default the storage order of the
  1307. * Columns in the database, however this order can be influenced by setting
  1308. * the ColumnOrder via {@link Database#setColumnOrder} prior to opening the
  1309. * Table. The {@link #asRow} method can be used to easily convert a row Map into the
  1310. * appropriate row array for this Table.
  1311. * <p>
  1312. * Note, if this table has an auto-number column, the value generated will be
  1313. * put back into the given row array (assuming the given row array is at
  1314. * least as long as the number of Columns in this Table).
  1315. *
  1316. * @param row row values for a single row. the given row array will be
  1317. * modified if this table contains an auto-number column,
  1318. * otherwise it will not be modified.
  1319. * @usage _general_method_
  1320. */
  1321. public void addRow(Object... row) throws IOException {
  1322. addRows(Collections.singletonList(row), _singleRowBufferH);
  1323. }
  1324. /**
  1325. * Add multiple rows to this table, only writing to disk after all
  1326. * rows have been written, and every time a data page is filled. This
  1327. * is much more efficient than calling <code>addRow</code> multiple times.
  1328. * <p>
  1329. * Note, if this table has an auto-number column, the values written will be
  1330. * put back into the given row arrays (assuming the given row array is at
  1331. * least as long as the number of Columns in this Table).
  1332. *
  1333. * @see #addRow(Object...) for more details on row arrays
  1334. *
  1335. * @param rows List of Object[] row values. the rows will be modified if
  1336. * this table contains an auto-number column, otherwise they
  1337. * will not be modified.
  1338. * @usage _general_method_
  1339. */
  1340. public void addRows(List<? extends Object[]> rows) throws IOException {
  1341. addRows(rows, _multiRowBufferH);
  1342. }
  1343. /**
  1344. * Add multiple rows to this table, only writing to disk after all
  1345. * rows have been written, and every time a data page is filled.
  1346. * @param inRows List of Object[] row values
  1347. * @param writeRowBufferH TempBufferHolder used to generate buffers for
  1348. * writing the row data
  1349. */
  1350. private void addRows(List<? extends Object[]> inRows,
  1351. TempBufferHolder writeRowBufferH)
  1352. throws IOException
  1353. {
  1354. if(inRows.isEmpty()) {
  1355. return;
  1356. }
  1357. // copy the input rows to a modifiable list so we can update the elements
  1358. List<Object[]> rows = new ArrayList<Object[]>(inRows);
  1359. ByteBuffer[] rowData = new ByteBuffer[rows.size()];
  1360. for (int i = 0; i < rows.size(); i++) {
  1361. // we need to make sure the row is the right length and is an Object[]
  1362. // (fill with null if too short). note, if the row is copied the caller
  1363. // will not be able to access any generated auto-number value, but if
  1364. // they need that info they should use a row array of the right
  1365. // size/type!
  1366. Object[] row = rows.get(i);
  1367. if((row.length < _columns.size()) || (row.getClass() != Object[].class)) {
  1368. row = dupeRow(row, _columns.size());
  1369. // we copied the row, so put the copy back into the rows list
  1370. rows.set(i, row);
  1371. }
  1372. // fill in autonumbers
  1373. handleAutoNumbersForAdd(row);
  1374. // write the row of data to a temporary buffer
  1375. rowData[i] = createRow(row, writeRowBufferH.getPageBuffer(getPageChannel()));
  1376. if (rowData[i].limit() > getFormat().MAX_ROW_SIZE) {
  1377. throw new IOException("Row size " + rowData[i].limit() +
  1378. " is too large");
  1379. }
  1380. }
  1381. ByteBuffer dataPage = null;
  1382. int pageNumber = PageChannel.INVALID_PAGE_NUMBER;
  1383. for (int i = 0; i < rowData.length; i++) {
  1384. int rowSize = rowData[i].remaining();
  1385. Object[] row = rows.get(i);
  1386. // handle foreign keys before adding to table
  1387. _fkEnforcer.addRow(row);
  1388. // get page with space
  1389. dataPage = findFreeRowSpace(rowSize, dataPage, pageNumber);
  1390. pageNumber = _addRowBufferH.getPageNumber();
  1391. // write out the row data
  1392. int rowNum = addDataPageRow(dataPage, rowSize, getFormat(), 0);
  1393. dataPage.put(rowData[i]);
  1394. // update the indexes
  1395. RowId rowId = new RowId(pageNumber, rowNum);
  1396. for(IndexData indexData : _indexDatas) {
  1397. indexData.addRow(row, rowId);
  1398. }
  1399. }
  1400. writeDataPage(dataPage, pageNumber);
  1401. // Update tdef page
  1402. updateTableDefinition(rows.size());
  1403. }
  1404. /**
  1405. * Updates the current row to the new values.
  1406. * <p>
  1407. * Note, if this table has an auto-number column(s), the existing value(s)
  1408. * will be maintained, unchanged.
  1409. *
  1410. * @param row new row values for the current row.
  1411. * @usage _general_method_
  1412. */
  1413. public void updateCurrentRow(Object... row) throws IOException {
  1414. getInternalCursor().updateCurrentRow(row);
  1415. }
  1416. /**
  1417. * Update the row on which the given rowState is currently positioned.
  1418. * <p>
  1419. * Note, this method is not generally meant to be used directly. You should
  1420. * use the {@link #updateCurrentRow} method or use the Cursor class, which
  1421. * allows for more complex table interactions, e.g.
  1422. * {@link Cursor#setCurrentRowValue} and {@link Cursor#updateCurrentRow}.
  1423. * @usage _advanced_method_
  1424. */
  1425. public void updateRow(RowState rowState, RowId rowId, Object... row)
  1426. throws IOException
  1427. {
  1428. requireValidRowId(rowId);
  1429. // ensure that the relevant row state is up-to-date
  1430. ByteBuffer rowBuffer = positionAtRowData(rowState, rowId);
  1431. int oldRowSize = rowBuffer.remaining();
  1432. requireNonDeletedRow(rowState, rowId);
  1433. // we need to make sure the row is the right length & type (fill with
  1434. // null if too short).
  1435. if((row.length < _columns.size()) || (row.getClass() != Object[].class)) {
  1436. row = dupeRow(row, _columns.size());
  1437. }
  1438. // hang on to the raw values of var length columns we are "keeping". this
  1439. // will allow us to re-use pre-written var length data, which can save
  1440. // space for things like long value columns.
  1441. Map<Column,byte[]> keepRawVarValues =
  1442. (!_varColumns.isEmpty() ? new HashMap<Column,byte[]>() : null);
  1443. for(Column column : _columns) {
  1444. if(_autoNumColumns.contains(column)) {
  1445. // fill in any auto-numbers (we don't allow autonumber values to be
  1446. // modified)
  1447. column.setRowValue(row, getRowColumn(getFormat(), rowBuffer, column,
  1448. rowState, null));
  1449. } else if(column.getRowValue(row) == Column.KEEP_VALUE) {
  1450. // fill in any "keep value" fields
  1451. column.setRowValue(row, getRowColumn(getFormat(), rowBuffer, column,
  1452. rowState, keepRawVarValues));
  1453. } else if(_indexColumns.contains(column)) {
  1454. // read row value to help update indexes
  1455. getRowColumn(getFormat(), rowBuffer, column, rowState, null);
  1456. }
  1457. }
  1458. // generate new row bytes
  1459. ByteBuffer newRowData = createRow(
  1460. row, _singleRowBufferH.getPageBuffer(getPageChannel()), oldRowSize,
  1461. keepRawVarValues);
  1462. if (newRowData.limit() > getFormat().MAX_ROW_SIZE) {
  1463. throw new IOException("Row size " + newRowData.limit() +
  1464. " is too large");
  1465. }
  1466. if(!_indexDatas.isEmpty()) {
  1467. Object[] oldRowValues = rowState.getRowValues();
  1468. // check foreign keys before actually updating
  1469. _fkEnforcer.updateRow(oldRowValues, row);
  1470. // delete old values from indexes
  1471. for(IndexData indexData : _indexDatas) {
  1472. indexData.deleteRow(oldRowValues, rowId);
  1473. }
  1474. }
  1475. // see if we can squeeze the new row data into the existing row
  1476. rowBuffer.reset();
  1477. int rowSize = newRowData.remaining();
  1478. ByteBuffer dataPage = null;
  1479. int pageNumber = PageChannel.INVALID_PAGE_NUMBER;
  1480. if(oldRowSize >= rowSize) {
  1481. // awesome, slap it in!
  1482. rowBuffer.put(newRowData);
  1483. // grab the page we just updated
  1484. dataPage = rowState.getFinalPage();
  1485. pageNumber = rowState.getFinalRowId().getPageNumber();
  1486. } else {
  1487. // bummer, need to find a new page for the data
  1488. dataPage = findFreeRowSpace(rowSize, null,
  1489. PageChannel.INVALID_PAGE_NUMBER);
  1490. pageNumber = _addRowBufferH.getPageNumber();
  1491. RowId headerRowId = rowState.getHeaderRowId();
  1492. ByteBuffer headerPage = rowState.getHeaderPage();
  1493. if(pageNumber == headerRowId.getPageNumber()) {
  1494. // new row is on the same page as header row, share page
  1495. dataPage = headerPage;
  1496. }
  1497. // write out the new row data (set the deleted flag on the new data row
  1498. // so that it is ignored during normal table traversal)
  1499. int rowNum = addDataPageRow(dataPage, rowSize, getFormat(),
  1500. DELETED_ROW_MASK);
  1501. dataPage.put(newRowData);
  1502. // write the overflow info into the header row and clear out the
  1503. // remaining header data
  1504. rowBuffer = PageChannel.narrowBuffer(
  1505. headerPage,
  1506. findRowStart(headerPage, headerRowId.getRowNumber(), getFormat()),
  1507. findRowEnd(headerPage, headerRowId.getRowNumber(), getFormat()));
  1508. rowBuffer.put((byte)rowNum);
  1509. ByteUtil.put3ByteInt(rowBuffer, pageNumber);
  1510. ByteUtil.clearRemaining(rowBuffer);
  1511. // set the overflow flag on the header row
  1512. int headerRowIndex = getRowStartOffset(headerRowId.getRowNumber(),
  1513. getFormat());
  1514. headerPage.putShort(headerRowIndex,
  1515. (short)(headerPage.getShort(headerRowIndex)
  1516. | OVERFLOW_ROW_MASK));
  1517. if(pageNumber != headerRowId.getPageNumber()) {
  1518. writeDataPage(headerPage, headerRowId.getPageNumber());
  1519. }
  1520. }
  1521. // update the indexes
  1522. for(IndexData indexData : _indexDatas) {
  1523. indexData.addRow(row, rowId);
  1524. }
  1525. writeDataPage(dataPage, pageNumber);
  1526. updateTableDefinition(0);
  1527. }
  1528. private ByteBuffer findFreeRowSpace(int rowSize, ByteBuffer dataPage,
  1529. int pageNumber)
  1530. throws IOException
  1531. {
  1532. // assume incoming page is modified
  1533. boolean modifiedPage = true;
  1534. if(dataPage == null) {
  1535. // find owned page w/ free space
  1536. dataPage = findFreeRowSpace(_ownedPages, _freeSpacePages,
  1537. _addRowBufferH);
  1538. if(dataPage == null) {
  1539. // No data pages exist (with free space). Create a new one.
  1540. return newDataPage();
  1541. }
  1542. // found a page, see if it will work
  1543. pageNumber = _addRowBufferH.getPageNumber();
  1544. // since we just loaded this page, it is not yet modified
  1545. modifiedPage = false;
  1546. }
  1547. if(!rowFitsOnDataPage(rowSize, dataPage, getFormat())) {
  1548. // Last data page is full. Write old one and create a new one.
  1549. if(modifiedPage) {
  1550. writeDataPage(dataPage, pageNumber);
  1551. }
  1552. _freeSpacePages.removePageNumber(pageNumber, true);
  1553. dataPage = newDataPage();
  1554. }
  1555. return dataPage;
  1556. }
  1557. static ByteBuffer findFreeRowSpace(
  1558. UsageMap ownedPages, UsageMap freeSpacePages,
  1559. TempPageHolder rowBufferH)
  1560. throws IOException
  1561. {
  1562. // find last data page (Not bothering to check other pages for free
  1563. // space.)
  1564. UsageMap.PageCursor revPageCursor = ownedPages.cursor();
  1565. revPageCursor.afterLast();
  1566. while(true) {
  1567. int tmpPageNumber = revPageCursor.getPreviousPage();
  1568. if(tmpPageNumber < 0) {
  1569. break;
  1570. }
  1571. ByteBuffer dataPage = rowBufferH.setPage(ownedPages.getPageChannel(),
  1572. tmpPageNumber);
  1573. if(dataPage.get() == PageTypes.DATA) {
  1574. // found last data page, only use if actually listed in free space
  1575. // pages
  1576. if(freeSpacePages.containsPageNumber(tmpPageNumber)) {
  1577. return dataPage;
  1578. }
  1579. }
  1580. }
  1581. return null;
  1582. }
  1583. /**
  1584. * Updates the table definition after rows are modified.
  1585. */
  1586. private void updateTableDefinition(int rowCountInc) throws IOException
  1587. {
  1588. // load table definition
  1589. ByteBuffer tdefPage = _tableDefBufferH.setPage(getPageChannel(),
  1590. _tableDefPageNumber);
  1591. // make sure rowcount and autonumber are up-to-date
  1592. _rowCount += rowCountInc;
  1593. tdefPage.putInt(getFormat().OFFSET_NUM_ROWS, _rowCount);
  1594. tdefPage.putInt(getFormat().OFFSET_NEXT_AUTO_NUMBER, _lastLongAutoNumber);
  1595. int ctypeOff = getFormat().OFFSET_NEXT_COMPLEX_AUTO_NUMBER;
  1596. if(ctypeOff >= 0) {
  1597. tdefPage.putInt(ctypeOff, _lastComplexTypeAutoNumber);
  1598. }
  1599. // write any index changes
  1600. for (IndexData indexData : _indexDatas) {
  1601. // write the unique entry count for the index to the table definition
  1602. // page
  1603. tdefPage.putInt(indexData.getUniqueEntryCountOffset(),
  1604. indexData.getUniqueEntryCount());
  1605. // write the entry page for the index
  1606. indexData.update();
  1607. }
  1608. // write modified table definition
  1609. getPageChannel().writePage(tdefPage, _tableDefPageNumber);
  1610. }
  1611. /**
  1612. * Create a new data page
  1613. * @return Page number of the new page
  1614. */
  1615. private ByteBuffer newDataPage() throws IOException {
  1616. if (LOG.isDebugEnabled()) {
  1617. LOG.debug("Creating new data page");
  1618. }
  1619. ByteBuffer dataPage = _addRowBufferH.setNewPage(getPageChannel());
  1620. dataPage.put(PageTypes.DATA); //Page type
  1621. dataPage.put((byte) 1); //Unknown
  1622. dataPage.putShort((short)getFormat().DATA_PAGE_INITIAL_FREE_SPACE); //Free space in this page
  1623. dataPage.putInt(_tableDefPageNumber); //Page pointer to table definition
  1624. dataPage.putInt(0); //Unknown
  1625. dataPage.putShort((short)0); //Number of rows on this page
  1626. int pageNumber = _addRowBufferH.getPageNumber();
  1627. getPageChannel().writePage(dataPage, pageNumber);
  1628. _ownedPages.addPageNumber(pageNumber);
  1629. _freeSpacePages.addPageNumber(pageNumber);
  1630. return dataPage;
  1631. }
  1632. ByteBuffer createRow(Object[] rowArray, ByteBuffer buffer)
  1633. throws IOException
  1634. {
  1635. return createRow(rowArray, buffer, 0, Collections.<Column,byte[]>emptyMap());
  1636. }
  1637. /**
  1638. * Serialize a row of Objects into a byte buffer.
  1639. *
  1640. * @param rowArray row data, expected to be correct length for this table
  1641. * @param buffer buffer to which to write the row data
  1642. * @param minRowSize min size for result row
  1643. * @param rawVarValues optional, pre-written values for var length columns
  1644. * (enables re-use of previously written values).
  1645. * @return the given buffer, filled with the row data
  1646. */
  1647. private ByteBuffer createRow(Object[] rowArray, ByteBuffer buffer,
  1648. int minRowSize, Map<Column,byte[]> rawVarValues)
  1649. throws IOException
  1650. {
  1651. buffer.putShort(_maxColumnCount);
  1652. NullMask nullMask = new NullMask(_maxColumnCount);
  1653. //Fixed length column data comes first
  1654. int fixedDataStart = buffer.position();
  1655. int fixedDataEnd = fixedDataStart;
  1656. for (Column col : _columns) {
  1657. if(col.isVariableLength()) {
  1658. continue;
  1659. }
  1660. Object rowValue = col.getRowValue(rowArray);
  1661. if (col.getType() == DataType.BOOLEAN) {
  1662. if(Column.toBooleanValue(rowValue)) {
  1663. //Booleans are stored in the null mask
  1664. nullMask.markNotNull(col);
  1665. }
  1666. rowValue = null;
  1667. }
  1668. if(rowValue != null) {
  1669. // we have a value to write
  1670. nullMask.markNotNull(col);
  1671. // remainingRowLength is ignored when writing fixed length data
  1672. buffer.position(fixedDataStart + col.getFixedDataOffset());
  1673. buffer.put(col.write(rowValue, 0));
  1674. }
  1675. // always insert space for the entire fixed data column length
  1676. // (including null values), access expects the row to always be at least
  1677. // big enough to hold all fixed values
  1678. buffer.position(fixedDataStart + col.getFixedDataOffset() +
  1679. col.getLength());
  1680. // keep track of the end of fixed data
  1681. if(buffer.position() > fixedDataEnd) {
  1682. fixedDataEnd = buffer.position();
  1683. }
  1684. }
  1685. // reposition at end of fixed data
  1686. buffer.position(fixedDataEnd);
  1687. // only need this info if this table contains any var length data
  1688. if(_maxVarColumnCount > 0) {
  1689. int maxRowSize = getFormat().MAX_ROW_SIZE;
  1690. // figure out how much space remains for var length data. first,
  1691. // account for already written space
  1692. maxRowSize -= buffer.position();
  1693. // now, account for trailer space
  1694. int trailerSize = (nullMask.byteSize() + 4 + (_maxVarColumnCount * 2));
  1695. maxRowSize -= trailerSize;
  1696. // for each non-null long value column we need to reserve a small
  1697. // amount of space so that we don't end up running out of row space
  1698. // later by being too greedy
  1699. for (Column varCol : _varColumns) {
  1700. if((varCol.getType().isLongValue()) &&
  1701. (varCol.getRowValue(rowArray) != null)) {
  1702. maxRowSize -= getFormat().SIZE_LONG_VALUE_DEF;
  1703. }
  1704. }
  1705. //Now write out variable length column data
  1706. short[] varColumnOffsets = new short[_maxVarColumnCount];
  1707. int varColumnOffsetsIndex = 0;
  1708. for (Column varCol : _varColumns) {
  1709. short offset = (short) buffer.position();
  1710. Object rowValue = varCol.getRowValue(rowArray);
  1711. if (rowValue != null) {
  1712. // we have a value
  1713. nullMask.markNotNull(varCol);
  1714. byte[] rawValue = null;
  1715. ByteBuffer varDataBuf = null;
  1716. if(((rawValue = rawVarValues.get(varCol)) != null) &&
  1717. (rawValue.length <= maxRowSize)) {
  1718. // save time and potentially db space, re-use raw value
  1719. varDataBuf = ByteBuffer.wrap(rawValue);
  1720. } else {
  1721. // write column value
  1722. varDataBuf = varCol.write(rowValue, maxRowSize);
  1723. }
  1724. maxRowSize -= varDataBuf.remaining();
  1725. if(varCol.getType().isLongValue()) {
  1726. // we already accounted for some amount of the long value data
  1727. // above. add that space back so we don't double count
  1728. maxRowSize += getFormat().SIZE_LONG_VALUE_DEF;
  1729. }
  1730. buffer.put(varDataBuf);
  1731. }
  1732. // we do a loop here so that we fill in offsets for deleted columns
  1733. while(varColumnOffsetsIndex <= varCol.getVarLenTableIndex()) {
  1734. varColumnOffsets[varColumnOffsetsIndex++] = offset;
  1735. }
  1736. }
  1737. // fill in offsets for any remaining deleted columns
  1738. while(varColumnOffsetsIndex < varColumnOffsets.length) {
  1739. varColumnOffsets[varColumnOffsetsIndex++] = (short) buffer.position();
  1740. }
  1741. // record where we stopped writing
  1742. int eod = buffer.position();
  1743. // insert padding if necessary
  1744. padRowBuffer(buffer, minRowSize, trailerSize);
  1745. buffer.putShort((short) eod); //EOD marker
  1746. //Now write out variable length offsets
  1747. //Offsets are stored in reverse order
  1748. for (int i = _maxVarColumnCount - 1; i >= 0; i--) {
  1749. buffer.putShort(varColumnOffsets[i]);
  1750. }
  1751. buffer.putShort(_maxVarColumnCount); //Number of var length columns
  1752. } else {
  1753. // insert padding for row w/ no var cols
  1754. padRowBuffer(buffer, minRowSize, nullMask.byteSize());
  1755. }
  1756. nullMask.write(buffer); //Null mask
  1757. buffer.flip();
  1758. if (LOG.isDebugEnabled()) {
  1759. LOG.debug("Creating new data block:\n" + ByteUtil.toHexString(buffer, buffer.limit()));
  1760. }
  1761. return buffer;
  1762. }
  1763. /**
  1764. * Fill in all autonumber column values.
  1765. */
  1766. private void handleAutoNumbersForAdd(Object[] row)
  1767. throws IOException
  1768. {
  1769. if(_autoNumColumns.isEmpty()) {
  1770. return;
  1771. }
  1772. Object complexAutoNumber = null;
  1773. for(Column col : _autoNumColumns) {
  1774. // ignore given row value, use next autonumber
  1775. Column.AutoNumberGenerator autoNumGen = col.getAutoNumberGenerator();
  1776. Object rowValue = null;
  1777. if(autoNumGen.getType() != DataType.COMPLEX_TYPE) {
  1778. rowValue = autoNumGen.getNext(null);
  1779. } else {
  1780. // complex type auto numbers are shared across all complex columns
  1781. // in the row
  1782. complexAutoNumber = autoNumGen.getNext(complexAutoNumber);
  1783. rowValue = complexAutoNumber;
  1784. }
  1785. col.setRowValue(row, rowValue);
  1786. }
  1787. }
  1788. private static void padRowBuffer(ByteBuffer buffer, int minRowSize,
  1789. int trailerSize)
  1790. {
  1791. int pos = buffer.position();
  1792. if((pos + trailerSize) < minRowSize) {
  1793. // pad the row to get to the min byte size
  1794. int padSize = minRowSize - (pos + trailerSize);
  1795. ByteUtil.clearRange(buffer, pos, pos + padSize);
  1796. ByteUtil.forward(buffer, padSize);
  1797. }
  1798. }
  1799. /**
  1800. * @usage _general_method_
  1801. */
  1802. public int getRowCount() {
  1803. return _rowCount;
  1804. }
  1805. int getNextLongAutoNumber() {
  1806. // note, the saved value is the last one handed out, so pre-increment
  1807. return ++_lastLongAutoNumber;
  1808. }
  1809. int getLastLongAutoNumber() {
  1810. // gets the last used auto number (does not modify)
  1811. return _lastLongAutoNumber;
  1812. }
  1813. int getNextComplexTypeAutoNumber() {
  1814. // note, the saved value is the last one handed out, so pre-increment
  1815. return ++_lastComplexTypeAutoNumber;
  1816. }
  1817. int getLastComplexTypeAutoNumber() {
  1818. // gets the last used auto number (does not modify)
  1819. return _lastComplexTypeAutoNumber;
  1820. }
  1821. @Override
  1822. public String toString() {
  1823. StringBuilder rtn = new StringBuilder();
  1824. rtn.append("Type: " + _tableType +
  1825. ((_tableType == TYPE_USER) ? " (USER)" : " (SYSTEM)"));
  1826. rtn.append("\nName: " + _name);
  1827. rtn.append("\nRow count: " + _rowCount);
  1828. rtn.append("\nColumn count: " + _columns.size());
  1829. rtn.append("\nIndex (data) count: " + _indexCount);
  1830. rtn.append("\nLogical Index count: " + _logicalIndexCount);
  1831. rtn.append("\nColumns:\n");
  1832. for(Column col : _columns) {
  1833. rtn.append(col);
  1834. }
  1835. rtn.append("\nIndexes:\n");
  1836. for(Index index : _indexes) {
  1837. rtn.append(index);
  1838. }
  1839. rtn.append("\nOwned pages: " + _ownedPages + "\n");
  1840. return rtn.toString();
  1841. }
  1842. /**
  1843. * @return A simple String representation of the entire table in
  1844. * tab-delimited format
  1845. * @usage _general_method_
  1846. */
  1847. public String display() throws IOException {
  1848. return display(Long.MAX_VALUE);
  1849. }
  1850. /**
  1851. * @param limit Maximum number of rows to display
  1852. * @return A simple String representation of the entire table in
  1853. * tab-delimited format
  1854. * @usage _general_method_
  1855. */
  1856. public String display(long limit) throws IOException {
  1857. reset();
  1858. StringBuilder rtn = new StringBuilder();
  1859. for(Iterator<Column> iter = _columns.iterator(); iter.hasNext(); ) {
  1860. Column col = iter.next();
  1861. rtn.append(col.getName());
  1862. if (iter.hasNext()) {
  1863. rtn.append("\t");
  1864. }
  1865. }
  1866. rtn.append("\n");
  1867. Map<String, Object> row;
  1868. int rowCount = 0;
  1869. while ((rowCount++ < limit) && (row = getNextRow()) != null) {
  1870. for(Iterator<Object> iter = row.values().iterator(); iter.hasNext(); ) {
  1871. Object obj = iter.next();
  1872. if (obj instanceof byte[]) {
  1873. byte[] b = (byte[]) obj;
  1874. rtn.append(ByteUtil.toHexString(b));
  1875. //This block can be used to easily dump a binary column to a file
  1876. /*java.io.File f = java.io.File.createTempFile("ole", ".bin");
  1877. java.io.FileOutputStream out = new java.io.FileOutputStream(f);
  1878. out.write(b);
  1879. out.flush();
  1880. out.close();*/
  1881. } else {
  1882. rtn.append(String.valueOf(obj));
  1883. }
  1884. if (iter.hasNext()) {
  1885. rtn.append("\t");
  1886. }
  1887. }
  1888. rtn.append("\n");
  1889. }
  1890. return rtn.toString();
  1891. }
  1892. /**
  1893. * Updates free space and row info for a new row of the given size in the
  1894. * given data page. Positions the page for writing the row data.
  1895. * @return the row number of the new row
  1896. * @usage _advanced_method_
  1897. */
  1898. public static int addDataPageRow(ByteBuffer dataPage,
  1899. int rowSize,
  1900. JetFormat format,
  1901. int rowFlags)
  1902. {
  1903. int rowSpaceUsage = getRowSpaceUsage(rowSize, format);
  1904. // Decrease free space record.
  1905. short freeSpaceInPage = dataPage.getShort(format.OFFSET_FREE_SPACE);
  1906. dataPage.putShort(format.OFFSET_FREE_SPACE, (short) (freeSpaceInPage -
  1907. rowSpaceUsage));
  1908. // Increment row count record.
  1909. short rowCount = dataPage.getShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE);
  1910. dataPage.putShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE,
  1911. (short) (rowCount + 1));
  1912. // determine row position
  1913. short rowLocation = findRowEnd(dataPage, rowCount, format);
  1914. rowLocation -= rowSize;
  1915. // write row position
  1916. dataPage.putShort(getRowStartOffset(rowCount, format),
  1917. (short)(rowLocation | rowFlags));
  1918. // set position for row data
  1919. dataPage.position(rowLocation);
  1920. return rowCount;
  1921. }
  1922. /**
  1923. * Returns the row count for the current page. If the page is invalid
  1924. * ({@code null}) or the page is not a DATA page, 0 is returned.
  1925. */
  1926. static int getRowsOnDataPage(ByteBuffer rowBuffer, JetFormat format)
  1927. throws IOException
  1928. {
  1929. int rowsOnPage = 0;
  1930. if((rowBuffer != null) && (rowBuffer.get(0) == PageTypes.DATA)) {
  1931. rowsOnPage = rowBuffer.getShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE);
  1932. }
  1933. return rowsOnPage;
  1934. }
  1935. /**
  1936. * @throws IllegalStateException if the given rowId is invalid
  1937. */
  1938. private static void requireValidRowId(RowId rowId) {
  1939. if(!rowId.isValid()) {
  1940. throw new IllegalArgumentException("Given rowId is invalid: " + rowId);
  1941. }
  1942. }
  1943. /**
  1944. * @throws IllegalStateException if the given row is invalid or deleted
  1945. */
  1946. private static void requireNonDeletedRow(RowState rowState, RowId rowId) {
  1947. if(!rowState.isValid()) {
  1948. throw new IllegalArgumentException(
  1949. "Given rowId is invalid for this table: " + rowId);
  1950. }
  1951. if(rowState.isDeleted()) {
  1952. throw new IllegalStateException("Row is deleted: " + rowId);
  1953. }
  1954. }
  1955. /**
  1956. * @usage _advanced_method_
  1957. */
  1958. public static boolean isDeletedRow(short rowStart) {
  1959. return ((rowStart & DELETED_ROW_MASK) != 0);
  1960. }
  1961. /**
  1962. * @usage _advanced_method_
  1963. */
  1964. public static boolean isOverflowRow(short rowStart) {
  1965. return ((rowStart & OVERFLOW_ROW_MASK) != 0);
  1966. }
  1967. /**
  1968. * @usage _advanced_method_
  1969. */
  1970. public static short cleanRowStart(short rowStart) {
  1971. return (short)(rowStart & OFFSET_MASK);
  1972. }
  1973. /**
  1974. * @usage _advanced_method_
  1975. */
  1976. public static short findRowStart(ByteBuffer buffer, int rowNum,
  1977. JetFormat format)
  1978. {
  1979. return cleanRowStart(
  1980. buffer.getShort(getRowStartOffset(rowNum, format)));
  1981. }
  1982. /**
  1983. * @usage _advanced_method_
  1984. */
  1985. public static int getRowStartOffset(int rowNum, JetFormat format)
  1986. {
  1987. return format.OFFSET_ROW_START + (format.SIZE_ROW_LOCATION * rowNum);
  1988. }
  1989. /**
  1990. * @usage _advanced_method_
  1991. */
  1992. public static short findRowEnd(ByteBuffer buffer, int rowNum,
  1993. JetFormat format)
  1994. {
  1995. return (short)((rowNum == 0) ?
  1996. format.PAGE_SIZE :
  1997. cleanRowStart(
  1998. buffer.getShort(getRowEndOffset(rowNum, format))));
  1999. }
  2000. /**
  2001. * @usage _advanced_method_
  2002. */
  2003. public static int getRowEndOffset(int rowNum, JetFormat format)
  2004. {
  2005. return format.OFFSET_ROW_START + (format.SIZE_ROW_LOCATION * (rowNum - 1));
  2006. }
  2007. /**
  2008. * @usage _advanced_method_
  2009. */
  2010. public static int getRowSpaceUsage(int rowSize, JetFormat format)
  2011. {
  2012. return rowSize + format.SIZE_ROW_LOCATION;
  2013. }
  2014. /**
  2015. * @return the "AutoNumber" columns in the given collection of columns.
  2016. * @usage _advanced_method_
  2017. */
  2018. public static List<Column> getAutoNumberColumns(Collection<Column> columns) {
  2019. List<Column> autoCols = new ArrayList<Column>(1);
  2020. for(Column c : columns) {
  2021. if(c.isAutoNumber()) {
  2022. autoCols.add(c);
  2023. }
  2024. }
  2025. return (!autoCols.isEmpty() ? autoCols : Collections.<Column>emptyList());
  2026. }
  2027. /**
  2028. * Returns {@code true} if a row of the given size will fit on the given
  2029. * data page, {@code false} otherwise.
  2030. * @usage _advanced_method_
  2031. */
  2032. public static boolean rowFitsOnDataPage(
  2033. int rowLength, ByteBuffer dataPage, JetFormat format)
  2034. throws IOException
  2035. {
  2036. int rowSpaceUsage = getRowSpaceUsage(rowLength, format);
  2037. short freeSpaceInPage = dataPage.getShort(format.OFFSET_FREE_SPACE);
  2038. int rowsOnPage = getRowsOnDataPage(dataPage, format);
  2039. return ((rowSpaceUsage <= freeSpaceInPage) &&
  2040. (rowsOnPage < format.MAX_NUM_ROWS_ON_DATA_PAGE));
  2041. }
  2042. /**
  2043. * Duplicates and returns a row of data, optionally with a longer length
  2044. * filled with {@code null}.
  2045. */
  2046. static Object[] dupeRow(Object[] row, int newRowLength) {
  2047. Object[] copy = new Object[newRowLength];
  2048. System.arraycopy(row, 0, copy, 0, Math.min(row.length, newRowLength));
  2049. return copy;
  2050. }
  2051. /** various statuses for the row data */
  2052. private enum RowStatus {
  2053. INIT, INVALID_PAGE, INVALID_ROW, VALID, DELETED, NORMAL, OVERFLOW;
  2054. }
  2055. /** the phases the RowState moves through as the data is parsed */
  2056. private enum RowStateStatus {
  2057. INIT, AT_HEADER, AT_FINAL;
  2058. }
  2059. /**
  2060. * Maintains the state of reading a row of data.
  2061. * @usage _advanced_class_
  2062. */
  2063. public final class RowState
  2064. {
  2065. /** Buffer used for reading the header row data pages */
  2066. private final TempPageHolder _headerRowBufferH;
  2067. /** the header rowId */
  2068. private RowId _headerRowId = RowId.FIRST_ROW_ID;
  2069. /** the number of rows on the header page */
  2070. private int _rowsOnHeaderPage;
  2071. /** the rowState status */
  2072. private RowStateStatus _status = RowStateStatus.INIT;
  2073. /** the row status */
  2074. private RowStatus _rowStatus = RowStatus.INIT;
  2075. /** buffer used for reading overflow pages */
  2076. private final TempPageHolder _overflowRowBufferH =
  2077. TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
  2078. /** the row buffer which contains the final data (after following any
  2079. overflow pointers) */
  2080. private ByteBuffer _finalRowBuffer;
  2081. /** the rowId which contains the final data (after following any overflow
  2082. pointers) */
  2083. private RowId _finalRowId = null;
  2084. /** true if the row values array has data */
  2085. private boolean _haveRowValues;
  2086. /** values read from the last row */
  2087. private final Object[] _rowValues;
  2088. /** null mask for the last row */
  2089. private NullMask _nullMask;
  2090. /** last modification count seen on the table we track this so that the
  2091. rowState can detect updates to the table and re-read any buffered
  2092. data */
  2093. private int _lastModCount;
  2094. /** optional error handler to use when row errors are encountered */
  2095. private ErrorHandler _errorHandler;
  2096. /** cached variable column offsets for jump-table based rows */
  2097. private short[] _varColOffsets;
  2098. private RowState(TempBufferHolder.Type headerType) {
  2099. _headerRowBufferH = TempPageHolder.newHolder(headerType);
  2100. _rowValues = new Object[Table.this.getColumnCount()];
  2101. _lastModCount = Table.this._modCount;
  2102. }
  2103. public Table getTable() {
  2104. return Table.this;
  2105. }
  2106. public ErrorHandler getErrorHandler() {
  2107. return((_errorHandler != null) ? _errorHandler :
  2108. getTable().getErrorHandler());
  2109. }
  2110. public void setErrorHandler(ErrorHandler newErrorHandler) {
  2111. _errorHandler = newErrorHandler;
  2112. }
  2113. public void reset() {
  2114. _finalRowId = null;
  2115. _finalRowBuffer = null;
  2116. _rowsOnHeaderPage = 0;
  2117. _status = RowStateStatus.INIT;
  2118. _rowStatus = RowStatus.INIT;
  2119. _varColOffsets = null;
  2120. _nullMask = null;
  2121. if(_haveRowValues) {
  2122. Arrays.fill(_rowValues, null);
  2123. _haveRowValues = false;
  2124. }
  2125. }
  2126. public boolean isUpToDate() {
  2127. return(Table.this._modCount == _lastModCount);
  2128. }
  2129. private void checkForModification() {
  2130. if(!isUpToDate()) {
  2131. reset();
  2132. _headerRowBufferH.invalidate();
  2133. _overflowRowBufferH.invalidate();
  2134. _lastModCount = Table.this._modCount;
  2135. }
  2136. }
  2137. private ByteBuffer getFinalPage()
  2138. throws IOException
  2139. {
  2140. if(_finalRowBuffer == null) {
  2141. // (re)load current page
  2142. _finalRowBuffer = getHeaderPage();
  2143. }
  2144. return _finalRowBuffer;
  2145. }
  2146. public RowId getFinalRowId() {
  2147. if(_finalRowId == null) {
  2148. _finalRowId = getHeaderRowId();
  2149. }
  2150. return _finalRowId;
  2151. }
  2152. private void setRowStatus(RowStatus rowStatus) {
  2153. _rowStatus = rowStatus;
  2154. }
  2155. public boolean isValid() {
  2156. return(_rowStatus.ordinal() >= RowStatus.VALID.ordinal());
  2157. }
  2158. public boolean isDeleted() {
  2159. return(_rowStatus == RowStatus.DELETED);
  2160. }
  2161. public boolean isOverflow() {
  2162. return(_rowStatus == RowStatus.OVERFLOW);
  2163. }
  2164. public boolean isHeaderPageNumberValid() {
  2165. return(_rowStatus.ordinal() > RowStatus.INVALID_PAGE.ordinal());
  2166. }
  2167. public boolean isHeaderRowNumberValid() {
  2168. return(_rowStatus.ordinal() > RowStatus.INVALID_ROW.ordinal());
  2169. }
  2170. private void setStatus(RowStateStatus status) {
  2171. _status = status;
  2172. }
  2173. public boolean isAtHeaderRow() {
  2174. return(_status.ordinal() >= RowStateStatus.AT_HEADER.ordinal());
  2175. }
  2176. public boolean isAtFinalRow() {
  2177. return(_status.ordinal() >= RowStateStatus.AT_FINAL.ordinal());
  2178. }
  2179. private Object setRowValue(int idx, Object value) {
  2180. _haveRowValues = true;
  2181. _rowValues[idx] = value;
  2182. return value;
  2183. }
  2184. public Object[] getRowValues() {
  2185. return dupeRow(_rowValues, _rowValues.length);
  2186. }
  2187. public NullMask getNullMask(ByteBuffer rowBuffer) throws IOException {
  2188. if(_nullMask == null) {
  2189. _nullMask = getRowNullMask(rowBuffer);
  2190. }
  2191. return _nullMask;
  2192. }
  2193. private short[] getVarColOffsets() {
  2194. return _varColOffsets;
  2195. }
  2196. private void setVarColOffsets(short[] varColOffsets) {
  2197. _varColOffsets = varColOffsets;
  2198. }
  2199. public RowId getHeaderRowId() {
  2200. return _headerRowId;
  2201. }
  2202. public int getRowsOnHeaderPage() {
  2203. return _rowsOnHeaderPage;
  2204. }
  2205. private ByteBuffer getHeaderPage()
  2206. throws IOException
  2207. {
  2208. checkForModification();
  2209. return _headerRowBufferH.getPage(getPageChannel());
  2210. }
  2211. private ByteBuffer setHeaderRow(RowId rowId)
  2212. throws IOException
  2213. {
  2214. checkForModification();
  2215. // don't do any work if we are already positioned correctly
  2216. if(isAtHeaderRow() && (getHeaderRowId().equals(rowId))) {
  2217. return(isValid() ? getHeaderPage() : null);
  2218. }
  2219. // rejigger everything
  2220. reset();
  2221. _headerRowId = rowId;
  2222. _finalRowId = rowId;
  2223. int pageNumber = rowId.getPageNumber();
  2224. int rowNumber = rowId.getRowNumber();
  2225. if((pageNumber < 0) || !_ownedPages.containsPageNumber(pageNumber)) {
  2226. setRowStatus(RowStatus.INVALID_PAGE);
  2227. return null;
  2228. }
  2229. _finalRowBuffer = _headerRowBufferH.setPage(getPageChannel(),
  2230. pageNumber);
  2231. _rowsOnHeaderPage = getRowsOnDataPage(_finalRowBuffer, getFormat());
  2232. if((rowNumber < 0) || (rowNumber >= _rowsOnHeaderPage)) {
  2233. setRowStatus(RowStatus.INVALID_ROW);
  2234. return null;
  2235. }
  2236. setRowStatus(RowStatus.VALID);
  2237. return _finalRowBuffer;
  2238. }
  2239. private ByteBuffer setOverflowRow(RowId rowId)
  2240. throws IOException
  2241. {
  2242. // this should never see modifications because it only happens within
  2243. // the positionAtRowData method
  2244. if(!isUpToDate()) {
  2245. throw new IllegalStateException("Table modified while searching?");
  2246. }
  2247. if(_rowStatus != RowStatus.OVERFLOW) {
  2248. throw new IllegalStateException("Row is not an overflow row?");
  2249. }
  2250. _finalRowId = rowId;
  2251. _finalRowBuffer = _overflowRowBufferH.setPage(getPageChannel(),
  2252. rowId.getPageNumber());
  2253. return _finalRowBuffer;
  2254. }
  2255. private Object handleRowError(Column column,
  2256. byte[] columnData,
  2257. Exception error)
  2258. throws IOException
  2259. {
  2260. return getErrorHandler().handleRowError(column, columnData,
  2261. this, error);
  2262. }
  2263. @Override
  2264. public String toString()
  2265. {
  2266. return "RowState: headerRowId = " + _headerRowId + ", finalRowId = " +
  2267. _finalRowId;
  2268. }
  2269. }
  2270. }