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

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209
  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.Iterator;
  32. import java.util.LinkedHashMap;
  33. import java.util.List;
  34. import java.util.Map;
  35. import org.apache.commons.logging.Log;
  36. import org.apache.commons.logging.LogFactory;
  37. /**
  38. * A single database table
  39. * <p>
  40. * Is not thread-safe.
  41. *
  42. * @author Tim McCune
  43. */
  44. public class Table
  45. implements Iterable<Map<String, Object>>
  46. {
  47. private static final Log LOG = LogFactory.getLog(Table.class);
  48. private static final short OFFSET_MASK = (short)0x1FFF;
  49. private static final short DELETED_ROW_MASK = (short)0x8000;
  50. private static final short OVERFLOW_ROW_MASK = (short)0x4000;
  51. private static final int MAX_BYTE = 256;
  52. /** Table type code for system tables */
  53. public static final byte TYPE_SYSTEM = 0x53;
  54. /** Table type code for user tables */
  55. public static final byte TYPE_USER = 0x4e;
  56. /** comparator which sorts variable length columns vased on their index into
  57. the variable length offset table */
  58. private static final Comparator<Column> VAR_LEN_COLUMN_COMPARATOR =
  59. new Comparator<Column>() {
  60. public int compare(Column c1, Column c2) {
  61. return ((c1.getVarLenTableIndex() < c2.getVarLenTableIndex()) ? -1 :
  62. ((c1.getVarLenTableIndex() > c2.getVarLenTableIndex()) ? 1 :
  63. 0));
  64. }
  65. };
  66. /** owning database */
  67. private final Database _database;
  68. /** Type of the table (either TYPE_SYSTEM or TYPE_USER) */
  69. private byte _tableType;
  70. /** Number of indexes on the table */
  71. private int _indexCount;
  72. /** Number of index slots for the table */
  73. private int _indexSlotCount;
  74. /** Number of rows in the table */
  75. private int _rowCount;
  76. /** last long auto number for the table */
  77. private int _lastLongAutoNumber;
  78. /** page number of the definition of this table */
  79. private final int _tableDefPageNumber;
  80. /** max Number of columns in the table (includes previous deletions) */
  81. private short _maxColumnCount;
  82. /** max Number of variable columns in the table */
  83. private short _maxVarColumnCount;
  84. /** List of columns in this table, ordered by column number */
  85. private List<Column> _columns = new ArrayList<Column>();
  86. /** List of variable length columns in this table, ordered by offset */
  87. private List<Column> _varColumns = new ArrayList<Column>();
  88. /** List of indexes on this table */
  89. private List<Index> _indexes = new ArrayList<Index>();
  90. /** Table name as stored in Database */
  91. private final String _name;
  92. /** Usage map of pages that this table owns */
  93. private UsageMap _ownedPages;
  94. /** Usage map of pages that this table owns with free space on them */
  95. private UsageMap _freeSpacePages;
  96. /** modification count for the table, keeps row-states up-to-date */
  97. private int _modCount;
  98. /** page buffer used to update data pages when adding rows */
  99. private final TempPageHolder _addRowBufferH =
  100. TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
  101. /** page buffer used to update the table def page */
  102. private final TempPageHolder _tableDefBufferH =
  103. TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
  104. /** buffer used to writing single rows of data */
  105. private final TempBufferHolder _singleRowBufferH =
  106. TempBufferHolder.newHolder(TempBufferHolder.Type.SOFT, true);
  107. /** "buffer" used to writing multi rows of data (will create new buffer on
  108. every call) */
  109. private final TempBufferHolder _multiRowBufferH =
  110. TempBufferHolder.newHolder(TempBufferHolder.Type.NONE, true);
  111. /** page buffer used to write out-of-line "long value" data */
  112. private final TempPageHolder _longValueBufferH =
  113. TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
  114. /** "big index support" is optional */
  115. private final boolean _useBigIndex;
  116. /** optional error handler to use when row errors are encountered */
  117. private ErrorHandler _tableErrorHandler;
  118. /** common cursor for iterating through the table, kept here for historic
  119. reasons */
  120. private Cursor _cursor;
  121. /**
  122. * Only used by unit tests
  123. */
  124. Table(boolean testing, List<Column> columns) throws IOException {
  125. if(!testing) {
  126. throw new IllegalArgumentException();
  127. }
  128. _database = null;
  129. _tableDefPageNumber = PageChannel.INVALID_PAGE_NUMBER;
  130. _name = null;
  131. _useBigIndex = true;
  132. setColumns(columns);
  133. }
  134. /**
  135. * @param database database which owns this table
  136. * @param tableBuffer Buffer to read the table with
  137. * @param pageNumber Page number of the table definition
  138. * @param name Table name
  139. * @param useBigIndex whether or not "big index support" should be enabled
  140. * for the table
  141. */
  142. protected Table(Database database, ByteBuffer tableBuffer,
  143. int pageNumber, String name, boolean useBigIndex)
  144. throws IOException
  145. {
  146. _database = database;
  147. _tableDefPageNumber = pageNumber;
  148. _name = name;
  149. _useBigIndex = useBigIndex;
  150. int nextPage = tableBuffer.getInt(getFormat().OFFSET_NEXT_TABLE_DEF_PAGE);
  151. ByteBuffer nextPageBuffer = null;
  152. while (nextPage != 0) {
  153. if (nextPageBuffer == null) {
  154. nextPageBuffer = getPageChannel().createPageBuffer();
  155. }
  156. getPageChannel().readPage(nextPageBuffer, nextPage);
  157. nextPage = nextPageBuffer.getInt(getFormat().OFFSET_NEXT_TABLE_DEF_PAGE);
  158. ByteBuffer newBuffer = getPageChannel().createBuffer(
  159. tableBuffer.capacity() + getFormat().PAGE_SIZE - 8);
  160. newBuffer.put(tableBuffer);
  161. newBuffer.put(nextPageBuffer.array(), 8, getFormat().PAGE_SIZE - 8);
  162. tableBuffer = newBuffer;
  163. tableBuffer.flip();
  164. }
  165. readTableDefinition(tableBuffer);
  166. tableBuffer = null;
  167. // setup common cursor
  168. _cursor = Cursor.createCursor(this);
  169. }
  170. /**
  171. * @return The name of the table
  172. */
  173. public String getName() {
  174. return _name;
  175. }
  176. public boolean doUseBigIndex() {
  177. return _useBigIndex;
  178. }
  179. public int getMaxColumnCount() {
  180. return _maxColumnCount;
  181. }
  182. public int getColumnCount() {
  183. return _columns.size();
  184. }
  185. public Database getDatabase() {
  186. return _database;
  187. }
  188. public JetFormat getFormat() {
  189. return getDatabase().getFormat();
  190. }
  191. public PageChannel getPageChannel() {
  192. return getDatabase().getPageChannel();
  193. }
  194. /**
  195. * Gets the currently configured ErrorHandler (always non-{@code null}).
  196. * This will be used to handle all errors unless overridden at the Cursor
  197. * level.
  198. */
  199. public ErrorHandler getErrorHandler() {
  200. return((_tableErrorHandler != null) ? _tableErrorHandler :
  201. getDatabase().getErrorHandler());
  202. }
  203. /**
  204. * Sets a new ErrorHandler. If {@code null}, resets to using the
  205. * ErrorHandler configured at the Database level.
  206. */
  207. public void setErrorHandler(ErrorHandler newErrorHandler) {
  208. _tableErrorHandler = newErrorHandler;
  209. }
  210. protected int getTableDefPageNumber() {
  211. return _tableDefPageNumber;
  212. }
  213. public RowState createRowState() {
  214. return new RowState(TempBufferHolder.Type.HARD);
  215. }
  216. protected UsageMap.PageCursor getOwnedPagesCursor() {
  217. return _ownedPages.cursor();
  218. }
  219. protected TempPageHolder getLongValueBuffer() {
  220. return _longValueBufferH;
  221. }
  222. /**
  223. * @return All of the columns in this table (unmodifiable List)
  224. */
  225. public List<Column> getColumns() {
  226. return Collections.unmodifiableList(_columns);
  227. }
  228. /**
  229. * @return the column with the given name
  230. */
  231. public Column getColumn(String name) {
  232. for(Column column : _columns) {
  233. if(column.getName().equalsIgnoreCase(name)) {
  234. return column;
  235. }
  236. }
  237. throw new IllegalArgumentException("Column with name " + name +
  238. " does not exist in this table");
  239. }
  240. /**
  241. * Only called by unit tests
  242. */
  243. private void setColumns(List<Column> columns) {
  244. _columns = columns;
  245. int colIdx = 0;
  246. int varLenIdx = 0;
  247. int fixedOffset = 0;
  248. for(Column col : _columns) {
  249. col.setColumnNumber((short)colIdx);
  250. col.setColumnIndex(colIdx++);
  251. if(col.isVariableLength()) {
  252. col.setVarLenTableIndex(varLenIdx++);
  253. _varColumns.add(col);
  254. } else {
  255. col.setFixedDataOffset(fixedOffset);
  256. fixedOffset += col.getType().getFixedSize();
  257. }
  258. }
  259. _maxColumnCount = (short)_columns.size();
  260. _maxVarColumnCount = (short)_varColumns.size();
  261. }
  262. /**
  263. * @return All of the Indexes on this table (unmodifiable List)
  264. */
  265. public List<Index> getIndexes() {
  266. return Collections.unmodifiableList(_indexes);
  267. }
  268. /**
  269. * @return the index with the given name
  270. */
  271. public Index getIndex(String name) {
  272. for(Index index : _indexes) {
  273. if(index.getName().equalsIgnoreCase(name)) {
  274. return index;
  275. }
  276. }
  277. throw new IllegalArgumentException("Index with name " + name +
  278. " does not exist on this table");
  279. }
  280. /**
  281. * Only called by unit tests
  282. */
  283. int getIndexSlotCount() {
  284. return _indexSlotCount;
  285. }
  286. /**
  287. * After calling this method, getNextRow will return the first row in the
  288. * table
  289. */
  290. public void reset() {
  291. _cursor.reset();
  292. }
  293. /**
  294. * Delete the current row (retrieved by a call to {@link #getNextRow()}).
  295. */
  296. public void deleteCurrentRow() throws IOException {
  297. _cursor.deleteCurrentRow();
  298. }
  299. /**
  300. * Delete the row on which the given rowState is currently positioned.
  301. */
  302. public void deleteRow(RowState rowState, RowId rowId) throws IOException {
  303. requireValidRowId(rowId);
  304. // ensure that the relevant row state is up-to-date
  305. ByteBuffer rowBuffer = positionAtRowHeader(rowState, rowId);
  306. requireNonDeletedRow(rowState, rowId);
  307. // delete flag always gets set in the "header" row (even if data is on
  308. // overflow row)
  309. int pageNumber = rowState.getHeaderRowId().getPageNumber();
  310. int rowNumber = rowState.getHeaderRowId().getRowNumber();
  311. // use any read rowValues to help update the indexes
  312. Object[] rowValues = (!_indexes.isEmpty() ?
  313. rowState.getRowValues() : null);
  314. int rowIndex = getRowStartOffset(rowNumber, getFormat());
  315. rowBuffer.putShort(rowIndex, (short)(rowBuffer.getShort(rowIndex)
  316. | DELETED_ROW_MASK | OVERFLOW_ROW_MASK));
  317. writeDataPage(rowBuffer, pageNumber);
  318. // update the indexes
  319. for(Index index : _indexes) {
  320. index.deleteRow(rowValues, rowId);
  321. }
  322. // make sure table def gets updated
  323. updateTableDefinition(-1);
  324. }
  325. /**
  326. * @return The next row in this table (Column name -> Column value)
  327. */
  328. public Map<String, Object> getNextRow() throws IOException {
  329. return getNextRow(null);
  330. }
  331. /**
  332. * @param columnNames Only column names in this collection will be returned
  333. * @return The next row in this table (Column name -> Column value)
  334. */
  335. public Map<String, Object> getNextRow(Collection<String> columnNames)
  336. throws IOException
  337. {
  338. return _cursor.getNextRow(columnNames);
  339. }
  340. /**
  341. * Reads a single column from the given row.
  342. */
  343. public Object getRowValue(RowState rowState, RowId rowId, Column column)
  344. throws IOException
  345. {
  346. if(this != column.getTable()) {
  347. throw new IllegalArgumentException(
  348. "Given column " + column + " is not from this table");
  349. }
  350. requireValidRowId(rowId);
  351. // position at correct row
  352. ByteBuffer rowBuffer = positionAtRowData(rowState, rowId);
  353. requireNonDeletedRow(rowState, rowId);
  354. return getRowColumn(getFormat(), rowBuffer, getRowNullMask(rowBuffer), column,
  355. rowState);
  356. }
  357. /**
  358. * Reads some columns from the given row.
  359. * @param columnNames Only column names in this collection will be returned
  360. */
  361. public Map<String, Object> getRow(
  362. RowState rowState, RowId rowId, Collection<String> columnNames)
  363. throws IOException
  364. {
  365. requireValidRowId(rowId);
  366. // position at correct row
  367. ByteBuffer rowBuffer = positionAtRowData(rowState, rowId);
  368. requireNonDeletedRow(rowState, rowId);
  369. return getRow(getFormat(), rowState, rowBuffer, getRowNullMask(rowBuffer), _columns,
  370. columnNames);
  371. }
  372. /**
  373. * Reads the row data from the given row buffer. Leaves limit unchanged.
  374. * Saves parsed row values to the given rowState.
  375. */
  376. private static Map<String, Object> getRow(
  377. JetFormat format,
  378. RowState rowState,
  379. ByteBuffer rowBuffer,
  380. NullMask nullMask,
  381. Collection<Column> columns,
  382. Collection<String> columnNames)
  383. throws IOException
  384. {
  385. Map<String, Object> rtn = new LinkedHashMap<String, Object>(
  386. columns.size());
  387. for(Column column : columns) {
  388. if((columnNames == null) || (columnNames.contains(column.getName()))) {
  389. // Add the value to the row data
  390. rtn.put(column.getName(),
  391. getRowColumn(format, rowBuffer, nullMask, column, rowState));
  392. }
  393. }
  394. return rtn;
  395. }
  396. /**
  397. * Reads the column data from the given row buffer. Leaves limit unchanged.
  398. * Caches the returned value in the rowState.
  399. */
  400. private static Object getRowColumn(JetFormat format,
  401. ByteBuffer rowBuffer,
  402. NullMask nullMask,
  403. Column column,
  404. RowState rowState)
  405. throws IOException
  406. {
  407. byte[] columnData = null;
  408. try {
  409. boolean isNull = nullMask.isNull(column);
  410. if(column.getType() == DataType.BOOLEAN) {
  411. // Boolean values are stored in the null mask. see note about
  412. // caching below
  413. return rowState.setRowValue(column.getColumnIndex(),
  414. Boolean.valueOf(!isNull));
  415. } else if(isNull) {
  416. // well, that's easy! (no need to update cache w/ null)
  417. return null;
  418. }
  419. // reset position to row start
  420. rowBuffer.reset();
  421. // locate the column data bytes
  422. int rowStart = rowBuffer.position();
  423. int colDataPos = 0;
  424. int colDataLen = 0;
  425. if(!column.isVariableLength()) {
  426. // read fixed length value (non-boolean at this point)
  427. int dataStart = rowStart + format.OFFSET_COLUMN_FIXED_DATA_ROW_OFFSET;
  428. colDataPos = dataStart + column.getFixedDataOffset();
  429. colDataLen = column.getType().getFixedSize(column.getLength());
  430. } else {
  431. int varDataStart;
  432. int varDataEnd;
  433. if(format.SIZE_ROW_VAR_COL_OFFSET == 2) {
  434. // read simple var length value
  435. int varColumnOffsetPos =
  436. (rowBuffer.limit() - nullMask.byteSize() - 4) -
  437. (column.getVarLenTableIndex() * 2);
  438. varDataStart = rowBuffer.getShort(varColumnOffsetPos);
  439. varDataEnd = rowBuffer.getShort(varColumnOffsetPos - 2);
  440. } else {
  441. // read jump-table based var length values
  442. short[] varColumnOffsets = readJumpTableVarColOffsets(
  443. rowState, rowBuffer, rowStart, nullMask);
  444. varDataStart = varColumnOffsets[column.getVarLenTableIndex()];
  445. varDataEnd = varColumnOffsets[column.getVarLenTableIndex() + 1];
  446. }
  447. colDataPos = rowStart + varDataStart;
  448. colDataLen = varDataEnd - varDataStart;
  449. }
  450. // grab the column data
  451. columnData = new byte[colDataLen];
  452. rowBuffer.position(colDataPos);
  453. rowBuffer.get(columnData);
  454. // parse the column data. we cache the row values in order to be able
  455. // to update the index on row deletion. note, most of the returned
  456. // values are immutable, except for binary data (returned as byte[]),
  457. // but binary data shouldn't be indexed anyway.
  458. return rowState.setRowValue(column.getColumnIndex(),
  459. column.read(columnData));
  460. } catch(Exception e) {
  461. // cache "raw" row value. see note about caching above
  462. rowState.setRowValue(column.getColumnIndex(),
  463. Column.rawDataWrapper(columnData));
  464. return rowState.handleRowError(column, columnData, e);
  465. }
  466. }
  467. static short[] readJumpTableVarColOffsets(
  468. RowState rowState, ByteBuffer rowBuffer, int rowStart,
  469. NullMask nullMask)
  470. {
  471. short[] varColOffsets = rowState.getVarColOffsets();
  472. if(varColOffsets != null) {
  473. return varColOffsets;
  474. }
  475. // calculate offsets using jump-table info
  476. int nullMaskSize = nullMask.byteSize();
  477. int rowEnd = rowStart + rowBuffer.remaining() - 1;
  478. int numVarCols = ByteUtil.getUnsignedByte(rowBuffer,
  479. rowEnd - nullMaskSize);
  480. varColOffsets = new short[numVarCols + 1];
  481. int rowLen = rowEnd - rowStart + 1;
  482. int numJumps = (rowLen - 1) / MAX_BYTE;
  483. int colOffset = rowEnd - nullMaskSize - numJumps - 1;
  484. // If last jump is a dummy value, ignore it
  485. if(((colOffset - rowStart - numVarCols) / MAX_BYTE) < numJumps) {
  486. numJumps--;
  487. }
  488. int jumpsUsed = 0;
  489. for(int i = 0; i < numVarCols + 1; i++) {
  490. if((jumpsUsed < numJumps) &&
  491. (i == ByteUtil.getUnsignedByte(
  492. rowBuffer, rowEnd - nullMaskSize-jumpsUsed - 1))) {
  493. jumpsUsed++;
  494. }
  495. varColOffsets[i] = (short)
  496. (ByteUtil.getUnsignedByte(rowBuffer, colOffset - i)
  497. + (jumpsUsed * MAX_BYTE));
  498. }
  499. rowState.setVarColOffsets(varColOffsets);
  500. return varColOffsets;
  501. }
  502. /**
  503. * Reads the null mask from the given row buffer. Leaves limit unchanged.
  504. */
  505. private NullMask getRowNullMask(ByteBuffer rowBuffer)
  506. throws IOException
  507. {
  508. // reset position to row start
  509. rowBuffer.reset();
  510. // Number of columns in this row
  511. int columnCount = ByteUtil.getUnsignedVarInt(
  512. rowBuffer, getFormat().SIZE_ROW_COLUMN_COUNT);
  513. // read null mask
  514. NullMask nullMask = new NullMask(columnCount);
  515. rowBuffer.position(rowBuffer.limit() - nullMask.byteSize()); //Null mask at end
  516. nullMask.read(rowBuffer);
  517. return nullMask;
  518. }
  519. /**
  520. * Sets a new buffer to the correct row header page using the given rowState
  521. * according to the given rowId. Deleted state is
  522. * determined, but overflow row pointers are not followed.
  523. *
  524. * @return a ByteBuffer of the relevant page, or null if row was invalid
  525. */
  526. public static ByteBuffer positionAtRowHeader(RowState rowState,
  527. RowId rowId)
  528. throws IOException
  529. {
  530. ByteBuffer rowBuffer = rowState.setHeaderRow(rowId);
  531. if(rowState.isAtHeaderRow()) {
  532. // this task has already been accomplished
  533. return rowBuffer;
  534. }
  535. if(!rowState.isValid()) {
  536. // this was an invalid page/row
  537. rowState.setStatus(RowStateStatus.AT_HEADER);
  538. return null;
  539. }
  540. // note, we don't use findRowStart here cause we need the unmasked value
  541. short rowStart = rowBuffer.getShort(
  542. getRowStartOffset(rowId.getRowNumber(),
  543. rowState.getTable().getFormat()));
  544. // check the deleted, overflow flags for the row (the "real" flags are
  545. // always set on the header row)
  546. RowStatus rowStatus = RowStatus.NORMAL;
  547. if(isDeletedRow(rowStart)) {
  548. rowStatus = RowStatus.DELETED;
  549. } else if(isOverflowRow(rowStart)) {
  550. rowStatus = RowStatus.OVERFLOW;
  551. }
  552. rowState.setRowStatus(rowStatus);
  553. rowState.setStatus(RowStateStatus.AT_HEADER);
  554. return rowBuffer;
  555. }
  556. /**
  557. * Sets the position and limit in a new buffer using the given rowState
  558. * according to the given row number and row end, following overflow row
  559. * pointers as necessary.
  560. *
  561. * @return a ByteBuffer narrowed to the actual row data, or null if row was
  562. * invalid or deleted
  563. */
  564. public static ByteBuffer positionAtRowData(RowState rowState,
  565. RowId rowId)
  566. throws IOException
  567. {
  568. positionAtRowHeader(rowState, rowId);
  569. if(!rowState.isValid() || rowState.isDeleted()) {
  570. // row is invalid or deleted
  571. rowState.setStatus(RowStateStatus.AT_FINAL);
  572. return null;
  573. }
  574. ByteBuffer rowBuffer = rowState.getFinalPage();
  575. int rowNum = rowState.getFinalRowId().getRowNumber();
  576. JetFormat format = rowState.getTable().getFormat();
  577. if(rowState.isAtFinalRow()) {
  578. // we've already found the final row data
  579. return PageChannel.narrowBuffer(
  580. rowBuffer,
  581. findRowStart(rowBuffer, rowNum, format),
  582. findRowEnd(rowBuffer, rowNum, format));
  583. }
  584. while(true) {
  585. // note, we don't use findRowStart here cause we need the unmasked value
  586. short rowStart = rowBuffer.getShort(getRowStartOffset(rowNum, format));
  587. short rowEnd = findRowEnd(rowBuffer, rowNum, format);
  588. // note, at this point we know the row is not deleted, so ignore any
  589. // subsequent deleted flags (as overflow rows are always marked deleted
  590. // anyway)
  591. boolean overflowRow = isOverflowRow(rowStart);
  592. // now, strip flags from rowStart offset
  593. rowStart = (short)(rowStart & OFFSET_MASK);
  594. if (overflowRow) {
  595. if((rowEnd - rowStart) < 4) {
  596. throw new IOException("invalid overflow row info");
  597. }
  598. // Overflow page. the "row" data in the current page points to
  599. // another page/row
  600. int overflowRowNum = ByteUtil.getUnsignedByte(rowBuffer, rowStart);
  601. int overflowPageNum = ByteUtil.get3ByteInt(rowBuffer, rowStart + 1);
  602. rowBuffer = rowState.setOverflowRow(
  603. new RowId(overflowPageNum, overflowRowNum));
  604. rowNum = overflowRowNum;
  605. } else {
  606. rowState.setStatus(RowStateStatus.AT_FINAL);
  607. return PageChannel.narrowBuffer(rowBuffer, rowStart, rowEnd);
  608. }
  609. }
  610. }
  611. /**
  612. * Calls <code>reset</code> on this table and returns an unmodifiable
  613. * Iterator which will iterate through all the rows of this table. Use of
  614. * the Iterator follows the same restrictions as a call to
  615. * <code>getNextRow</code>.
  616. * @throws IllegalStateException if an IOException is thrown by one of the
  617. * operations, the actual exception will be contained within
  618. */
  619. public Iterator<Map<String, Object>> iterator()
  620. {
  621. return iterator(null);
  622. }
  623. /**
  624. * Calls <code>reset</code> on this table and returns an unmodifiable
  625. * Iterator which will iterate through all the rows of this table, returning
  626. * only the given columns. Use of the Iterator follows the same
  627. * restrictions as a call to <code>getNextRow</code>.
  628. * @throws IllegalStateException if an IOException is thrown by one of the
  629. * operations, the actual exception will be contained within
  630. */
  631. public Iterator<Map<String, Object>> iterator(Collection<String> columnNames)
  632. {
  633. reset();
  634. return _cursor.iterator(columnNames);
  635. }
  636. /**
  637. * Writes a new table defined by the given columns to the database.
  638. * @return the first page of the new table's definition
  639. */
  640. public static int writeTableDefinition(
  641. List<Column> columns, PageChannel pageChannel, JetFormat format,
  642. Charset charset)
  643. throws IOException
  644. {
  645. // first, create the usage map page
  646. int usageMapPageNumber = pageChannel.writeNewPage(
  647. createUsageMapDefinitionBuffer(pageChannel, format));
  648. // next, determine how big the table def will be (in case it will be more
  649. // than one page)
  650. int totalTableDefSize = format.SIZE_TDEF_HEADER +
  651. (format.SIZE_COLUMN_DEF_BLOCK * columns.size()) +
  652. format.SIZE_TDEF_TRAILER;
  653. for(Column col : columns) {
  654. // we add the number of bytes for the column name and 2 bytes for the
  655. // length of the column name
  656. int nameByteLen = (col.getName().length() *
  657. JetFormat.TEXT_FIELD_UNIT_SIZE);
  658. totalTableDefSize += nameByteLen + 2;
  659. }
  660. // now, create the table definition
  661. ByteBuffer buffer = pageChannel.createBuffer(Math.max(totalTableDefSize,
  662. format.PAGE_SIZE));
  663. writeTableDefinitionHeader(buffer, columns, usageMapPageNumber,
  664. totalTableDefSize, format);
  665. writeColumnDefinitions(buffer, columns, format, charset);
  666. //End of tabledef
  667. buffer.put((byte) 0xff);
  668. buffer.put((byte) 0xff);
  669. // write table buffer to database
  670. int tdefPageNumber = PageChannel.INVALID_PAGE_NUMBER;
  671. if(totalTableDefSize <= format.PAGE_SIZE) {
  672. // easy case, fits on one page
  673. buffer.putShort(format.OFFSET_FREE_SPACE,
  674. (short)(buffer.remaining() - 8)); // overwrite page free space
  675. // Write the tdef page to disk.
  676. tdefPageNumber = pageChannel.writeNewPage(buffer);
  677. } else {
  678. // need to split across multiple pages
  679. ByteBuffer partialTdef = pageChannel.createPageBuffer();
  680. buffer.rewind();
  681. int nextTdefPageNumber = PageChannel.INVALID_PAGE_NUMBER;
  682. while(buffer.hasRemaining()) {
  683. // reset for next write
  684. partialTdef.clear();
  685. if(tdefPageNumber == PageChannel.INVALID_PAGE_NUMBER) {
  686. // this is the first page. note, the first page already has the
  687. // page header, so no need to write it here
  688. tdefPageNumber = pageChannel.allocateNewPage();
  689. nextTdefPageNumber = tdefPageNumber;
  690. } else {
  691. // write page header
  692. writeTablePageHeader(partialTdef);
  693. }
  694. // copy the next page of tdef bytes
  695. int curTdefPageNumber = nextTdefPageNumber;
  696. int writeLen = Math.min(partialTdef.remaining(), buffer.remaining());
  697. partialTdef.put(buffer.array(), buffer.position(), writeLen);
  698. ByteUtil.forward(buffer, writeLen);
  699. if(buffer.hasRemaining()) {
  700. // need a next page
  701. nextTdefPageNumber = pageChannel.allocateNewPage();
  702. partialTdef.putInt(format.OFFSET_NEXT_TABLE_DEF_PAGE,
  703. nextTdefPageNumber);
  704. }
  705. // update page free space
  706. partialTdef.putShort(format.OFFSET_FREE_SPACE,
  707. (short)(partialTdef.remaining() - 8)); // overwrite page free space
  708. // write partial page to disk
  709. pageChannel.writePage(partialTdef, curTdefPageNumber);
  710. }
  711. }
  712. return tdefPageNumber;
  713. }
  714. /**
  715. * @param buffer Buffer to write to
  716. * @param columns List of Columns in the table
  717. */
  718. private static void writeTableDefinitionHeader(
  719. ByteBuffer buffer, List<Column> columns,
  720. int usageMapPageNumber, int totalTableDefSize, JetFormat format)
  721. throws IOException
  722. {
  723. //Start writing the tdef
  724. writeTablePageHeader(buffer);
  725. buffer.putInt(totalTableDefSize); //Length of table def
  726. buffer.put((byte) 0x59); //Unknown
  727. buffer.put((byte) 0x06); //Unknown
  728. buffer.putShort((short) 0); //Unknown
  729. buffer.putInt(0); //Number of rows
  730. buffer.putInt(0); //Last Autonumber
  731. buffer.put((byte) 1); // this makes autonumbering work in access
  732. for (int i = 0; i < 15; i++) { //Unknown
  733. buffer.put((byte) 0);
  734. }
  735. buffer.put(Table.TYPE_USER); //Table type
  736. buffer.putShort((short) columns.size()); //Max columns a row will have
  737. buffer.putShort(Column.countVariableLength(columns)); //Number of variable columns in table
  738. buffer.putShort((short) columns.size()); //Number of columns in table
  739. buffer.putInt(0); //Number of indexes in table
  740. buffer.putInt(0); //Number of indexes in table
  741. buffer.put((byte) 0); //Usage map row number
  742. ByteUtil.put3ByteInt(buffer, usageMapPageNumber); //Usage map page number
  743. buffer.put((byte) 1); //Free map row number
  744. ByteUtil.put3ByteInt(buffer, usageMapPageNumber); //Free map page number
  745. if (LOG.isDebugEnabled()) {
  746. int position = buffer.position();
  747. buffer.rewind();
  748. LOG.debug("Creating new table def block:\n" + ByteUtil.toHexString(
  749. buffer, format.SIZE_TDEF_HEADER));
  750. buffer.position(position);
  751. }
  752. }
  753. /**
  754. * Writes the page header for a table definition page
  755. * @param buffer Buffer to write to
  756. */
  757. private static void writeTablePageHeader(ByteBuffer buffer)
  758. {
  759. buffer.put(PageTypes.TABLE_DEF); //Page type
  760. buffer.put((byte) 0x01); //Unknown
  761. buffer.put((byte) 0); //Unknown
  762. buffer.put((byte) 0); //Unknown
  763. buffer.putInt(0); //Next TDEF page pointer
  764. }
  765. /**
  766. * @param buffer Buffer to write to
  767. * @param columns List of Columns to write definitions for
  768. */
  769. private static void writeColumnDefinitions(
  770. ByteBuffer buffer, List<Column> columns, JetFormat format,
  771. Charset charset)
  772. throws IOException
  773. {
  774. short columnNumber = (short) 0;
  775. short fixedOffset = (short) 0;
  776. short variableOffset = (short) 0;
  777. // we specifically put the "long variable" values after the normal
  778. // variable length values so that we have a better chance of fitting it
  779. // all (because "long variable" values can go in separate pages)
  780. short longVariableOffset =
  781. Column.countNonLongVariableLength(columns);
  782. for (Column col : columns) {
  783. int position = buffer.position();
  784. buffer.put(col.getType().getValue());
  785. buffer.put((byte) 0x59); //Unknown
  786. buffer.put((byte) 0x06); //Unknown
  787. buffer.putShort((short) 0); //Unknown
  788. buffer.putShort(columnNumber); //Column Number
  789. if (col.isVariableLength()) {
  790. if(!col.getType().isLongValue()) {
  791. buffer.putShort(variableOffset++);
  792. } else {
  793. buffer.putShort(longVariableOffset++);
  794. }
  795. } else {
  796. buffer.putShort((short) 0);
  797. }
  798. buffer.putShort(columnNumber); //Column Number again
  799. if(col.getType().getHasScalePrecision()) {
  800. buffer.put(col.getPrecision()); // numeric precision
  801. buffer.put(col.getScale()); // numeric scale
  802. } else {
  803. buffer.put((byte) 0x00); //unused
  804. buffer.put((byte) 0x00); //unused
  805. }
  806. buffer.putShort((short) 0); //Unknown
  807. buffer.put(getColumnBitFlags(col)); // misc col flags
  808. if (col.isCompressedUnicode()) { //Compressed
  809. buffer.put((byte) 1);
  810. } else {
  811. buffer.put((byte) 0);
  812. }
  813. buffer.putInt(0); //Unknown, but always 0.
  814. //Offset for fixed length columns
  815. if (col.isVariableLength()) {
  816. buffer.putShort((short) 0);
  817. } else {
  818. buffer.putShort(fixedOffset);
  819. fixedOffset += col.getType().getFixedSize(col.getLength());
  820. }
  821. if(!col.getType().isLongValue()) {
  822. buffer.putShort(col.getLength()); //Column length
  823. } else {
  824. buffer.putShort((short)0x0000); // unused
  825. }
  826. columnNumber++;
  827. if (LOG.isDebugEnabled()) {
  828. LOG.debug("Creating new column def block\n" + ByteUtil.toHexString(
  829. buffer, position, format.SIZE_COLUMN_DEF_BLOCK));
  830. }
  831. }
  832. for (Column col : columns) {
  833. writeName(buffer, col.getName(), charset);
  834. }
  835. }
  836. /**
  837. * Writes the given name into the given buffer in the format as expected by
  838. * {@link #readName}.
  839. */
  840. private static void writeName(ByteBuffer buffer, String name,
  841. Charset charset)
  842. {
  843. ByteBuffer encName = Column.encodeUncompressedText(name, charset);
  844. buffer.putShort((short) encName.remaining());
  845. buffer.put(encName);
  846. }
  847. /**
  848. * Constructs a byte containing the flags for the given column.
  849. */
  850. private static byte getColumnBitFlags(Column col) {
  851. byte flags = Column.UNKNOWN_FLAG_MASK;
  852. if(!col.isVariableLength()) {
  853. flags |= Column.FIXED_LEN_FLAG_MASK;
  854. }
  855. if(col.isAutoNumber()) {
  856. flags |= col.getAutoNumberGenerator().getColumnFlags();
  857. }
  858. return flags;
  859. }
  860. /**
  861. * Create the usage map definition page buffer. The "used pages" map is in
  862. * row 0, the "pages with free space" map is in row 1.
  863. */
  864. private static ByteBuffer createUsageMapDefinitionBuffer(
  865. PageChannel pageChannel, JetFormat format)
  866. throws IOException
  867. {
  868. int usageMapRowLength = format.OFFSET_USAGE_MAP_START +
  869. format.USAGE_MAP_TABLE_BYTE_LENGTH;
  870. int freeSpace = format.PAGE_INITIAL_FREE_SPACE
  871. - (2 * getRowSpaceUsage(usageMapRowLength, format));
  872. ByteBuffer rtn = pageChannel.createPageBuffer();
  873. rtn.put(PageTypes.DATA);
  874. rtn.put((byte) 0x1); //Unknown
  875. rtn.putShort((short)freeSpace); //Free space in page
  876. rtn.putInt(0); //Table definition
  877. rtn.putInt(0); //Unknown
  878. rtn.putShort((short) 2); //Number of records on this page
  879. // write two rows of usage map definitions
  880. int rowStart = findRowEnd(rtn, 0, format) - usageMapRowLength;
  881. for(int i = 0; i < 2; ++i) {
  882. rtn.putShort(getRowStartOffset(i, format), (short)rowStart);
  883. if(i == 0) {
  884. // initial "usage pages" map definition
  885. rtn.put(rowStart, UsageMap.MAP_TYPE_REFERENCE);
  886. } else {
  887. // initial "pages with free space" map definition
  888. rtn.put(rowStart, UsageMap.MAP_TYPE_INLINE);
  889. }
  890. rowStart -= usageMapRowLength;
  891. }
  892. return rtn;
  893. }
  894. /**
  895. * Read the table definition
  896. */
  897. private void readTableDefinition(ByteBuffer tableBuffer) throws IOException
  898. {
  899. if (LOG.isDebugEnabled()) {
  900. tableBuffer.rewind();
  901. LOG.debug("Table def block:\n" + ByteUtil.toHexString(tableBuffer,
  902. getFormat().SIZE_TDEF_HEADER));
  903. }
  904. _rowCount = tableBuffer.getInt(getFormat().OFFSET_NUM_ROWS);
  905. _lastLongAutoNumber = tableBuffer.getInt(getFormat().OFFSET_NEXT_AUTO_NUMBER);
  906. _tableType = tableBuffer.get(getFormat().OFFSET_TABLE_TYPE);
  907. _maxColumnCount = tableBuffer.getShort(getFormat().OFFSET_MAX_COLS);
  908. _maxVarColumnCount = tableBuffer.getShort(getFormat().OFFSET_NUM_VAR_COLS);
  909. short columnCount = tableBuffer.getShort(getFormat().OFFSET_NUM_COLS);
  910. _indexSlotCount = tableBuffer.getInt(getFormat().OFFSET_NUM_INDEX_SLOTS);
  911. _indexCount = tableBuffer.getInt(getFormat().OFFSET_NUM_INDEXES);
  912. int rowNum = ByteUtil.getUnsignedByte(
  913. tableBuffer, getFormat().OFFSET_OWNED_PAGES);
  914. int pageNum = ByteUtil.get3ByteInt(tableBuffer, getFormat().OFFSET_OWNED_PAGES + 1);
  915. _ownedPages = UsageMap.read(getDatabase(), pageNum, rowNum, false);
  916. rowNum = ByteUtil.getUnsignedByte(
  917. tableBuffer, getFormat().OFFSET_FREE_SPACE_PAGES);
  918. pageNum = ByteUtil.get3ByteInt(tableBuffer, getFormat().OFFSET_FREE_SPACE_PAGES + 1);
  919. _freeSpacePages = UsageMap.read(getDatabase(), pageNum, rowNum, false);
  920. for (int i = 0; i < _indexCount; i++) {
  921. int uniqueEntryCountOffset =
  922. (getFormat().OFFSET_INDEX_DEF_BLOCK +
  923. (i * getFormat().SIZE_INDEX_DEFINITION) + 4);
  924. int uniqueEntryCount = tableBuffer.getInt(uniqueEntryCountOffset);
  925. _indexes.add(createIndex(uniqueEntryCount, uniqueEntryCountOffset));
  926. }
  927. int colOffset = getFormat().OFFSET_INDEX_DEF_BLOCK +
  928. _indexCount * getFormat().SIZE_INDEX_DEFINITION;
  929. for (int i = 0; i < columnCount; i++) {
  930. Column column = new Column(this, tableBuffer,
  931. colOffset + (i * getFormat().SIZE_COLUMN_HEADER));
  932. _columns.add(column);
  933. if(column.isVariableLength()) {
  934. // also shove it in the variable columns list, which is ordered
  935. // differently from the _columns list
  936. _varColumns.add(column);
  937. }
  938. }
  939. tableBuffer.position(colOffset +
  940. (columnCount * getFormat().SIZE_COLUMN_HEADER));
  941. for (int i = 0; i < columnCount; i++) {
  942. Column column = _columns.get(i);
  943. column.setName(readName(tableBuffer));
  944. }
  945. Collections.sort(_columns);
  946. // setup the data index for the columns
  947. int colIdx = 0;
  948. for(Column col : _columns) {
  949. col.setColumnIndex(colIdx++);
  950. }
  951. // sort variable length columns based on their index into the variable
  952. // length offset table, because we will write the columns in this order
  953. Collections.sort(_varColumns, VAR_LEN_COLUMN_COMPARATOR);
  954. int idxOffset = tableBuffer.position();
  955. tableBuffer.position(idxOffset +
  956. (getFormat().OFFSET_INDEX_NUMBER_BLOCK * _indexCount));
  957. // if there are more index slots than indexes, the initial slots are
  958. // always empty/invalid, so we skip that data
  959. int firstRealIdx = (_indexSlotCount - _indexCount);
  960. for (int i = 0; i < _indexSlotCount; i++) {
  961. ByteUtil.forward(tableBuffer, getFormat().SKIP_BEFORE_INDEX_SLOT); //Forward past Unknown
  962. tableBuffer.getInt(); //Forward past alternate index number
  963. int indexNumber = tableBuffer.getInt();
  964. ByteUtil.forward(tableBuffer, 11);
  965. byte indexType = tableBuffer.get();
  966. ByteUtil.forward(tableBuffer, getFormat().SKIP_AFTER_INDEX_SLOT); //Skip past Unknown
  967. if(i < firstRealIdx) {
  968. // ignore this info
  969. continue;
  970. }
  971. Index index = _indexes.get(i - firstRealIdx);
  972. index.setIndexNumber(indexNumber);
  973. index.setIndexType(indexType);
  974. }
  975. // read actual index names
  976. for (int i = 0; i < _indexSlotCount; i++) {
  977. if(i < firstRealIdx) {
  978. // for each empty index slot, there is some weird sort of name, skip
  979. // it
  980. skipName(tableBuffer);
  981. continue;
  982. }
  983. _indexes.get(i - firstRealIdx)
  984. .setName(readName(tableBuffer));
  985. }
  986. int idxEndOffset = tableBuffer.position();
  987. Collections.sort(_indexes);
  988. // go back to index column info after sorting
  989. tableBuffer.position(idxOffset);
  990. for (int i = 0; i < _indexCount; i++) {
  991. ByteUtil.forward(tableBuffer, getFormat().SKIP_BEFORE_INDEX); //Forward past Unknown
  992. _indexes.get(i).read(tableBuffer, _columns);
  993. }
  994. // reset to end of index info
  995. tableBuffer.position(idxEndOffset);
  996. }
  997. /**
  998. * Creates an index with the given initial info.
  999. */
  1000. private Index createIndex(int uniqueEntryCount, int uniqueEntryCountOffset)
  1001. {
  1002. return(_useBigIndex ?
  1003. new BigIndex(this, uniqueEntryCount, uniqueEntryCountOffset) :
  1004. new SimpleIndex(this, uniqueEntryCount, uniqueEntryCountOffset));
  1005. }
  1006. /**
  1007. * Writes the given page data to the given page number, clears any other
  1008. * relevant buffers.
  1009. */
  1010. private void writeDataPage(ByteBuffer pageBuffer, int pageNumber)
  1011. throws IOException
  1012. {
  1013. // write the page data
  1014. getPageChannel().writePage(pageBuffer, pageNumber);
  1015. // possibly invalidate the add row buffer if a different data buffer is
  1016. // being written (e.g. this happens during deleteRow)
  1017. _addRowBufferH.possiblyInvalidate(pageNumber, pageBuffer);
  1018. // update modification count so any active RowStates can keep themselves
  1019. // up-to-date
  1020. ++_modCount;
  1021. }
  1022. /**
  1023. * Returns a name read from the buffer at the current position. The
  1024. * expected name format is the name length followed by the name
  1025. * encoded using the {@link JetFormat#CHARSET}
  1026. */
  1027. private String readName(ByteBuffer buffer) {
  1028. int nameLength = readNameLength(buffer);
  1029. byte[] nameBytes = new byte[nameLength];
  1030. buffer.get(nameBytes);
  1031. return Column.decodeUncompressedText(nameBytes,
  1032. getDatabase().getCharset());
  1033. }
  1034. /**
  1035. * Skips past a name int the buffer at the current position. The
  1036. * expected name format is the same as that for {@link #readName}.
  1037. */
  1038. private void skipName(ByteBuffer buffer) {
  1039. int nameLength = readNameLength(buffer);
  1040. ByteUtil.forward(buffer, nameLength);
  1041. }
  1042. /**
  1043. * Returns a name length read from the buffer at the current position.
  1044. */
  1045. private int readNameLength(ByteBuffer buffer) {
  1046. return ByteUtil.getUnsignedVarInt(buffer, getFormat().SIZE_NAME_LENGTH);
  1047. }
  1048. /**
  1049. * Converts a map of columnName -> columnValue to an array of row values
  1050. * appropriate for a call to {@link #addRow(Object...)}.
  1051. */
  1052. public Object[] asRow(Map<String,Object> rowMap) {
  1053. return asRow(rowMap, null);
  1054. }
  1055. /**
  1056. * Converts a map of columnName -> columnValue to an array of row values
  1057. * appropriate for a call to {@link #updateCurrentRow(Object...)}.
  1058. */
  1059. public Object[] asUpdateRow(Map<String,Object> rowMap) {
  1060. return asRow(rowMap, Column.KEEP_VALUE);
  1061. }
  1062. /**
  1063. * Converts a map of columnName -> columnValue to an array of row values.
  1064. */
  1065. private Object[] asRow(Map<String,Object> rowMap, Object defaultValue)
  1066. {
  1067. Object[] row = new Object[_columns.size()];
  1068. if(defaultValue != null) {
  1069. Arrays.fill(row, defaultValue);
  1070. }
  1071. if(rowMap == null) {
  1072. return row;
  1073. }
  1074. for(Column col : _columns) {
  1075. if(rowMap.containsKey(col.getName())) {
  1076. row[col.getColumnIndex()] = rowMap.get(col.getName());
  1077. }
  1078. }
  1079. return row;
  1080. }
  1081. /**
  1082. * Add a single row to this table and write it to disk
  1083. * <p>
  1084. * Note, if this table has an auto-number column, the value written will be
  1085. * put back into the given row array.
  1086. *
  1087. * @param row row values for a single row. the row will be modified if
  1088. * this table contains an auto-number column, otherwise it
  1089. * will not be modified.
  1090. */
  1091. public void addRow(Object... row) throws IOException {
  1092. addRows(Collections.singletonList(row), _singleRowBufferH);
  1093. }
  1094. /**
  1095. * Add multiple rows to this table, only writing to disk after all
  1096. * rows have been written, and every time a data page is filled. This
  1097. * is much more efficient than calling <code>addRow</code> multiple times.
  1098. * <p>
  1099. * Note, if this table has an auto-number column, the values written will be
  1100. * put back into the given row arrays.
  1101. *
  1102. * @param rows List of Object[] row values. the rows will be modified if
  1103. * this table contains an auto-number column, otherwise they
  1104. * will not be modified.
  1105. */
  1106. public void addRows(List<? extends Object[]> rows) throws IOException {
  1107. addRows(rows, _multiRowBufferH);
  1108. }
  1109. /**
  1110. * Add multiple rows to this table, only writing to disk after all
  1111. * rows have been written, and every time a data page is filled.
  1112. * @param inRows List of Object[] row values
  1113. * @param writeRowBufferH TempBufferHolder used to generate buffers for
  1114. * writing the row data
  1115. */
  1116. private void addRows(List<? extends Object[]> inRows,
  1117. TempBufferHolder writeRowBufferH)
  1118. throws IOException
  1119. {
  1120. if(inRows.isEmpty()) {
  1121. return;
  1122. }
  1123. // copy the input rows to a modifiable list so we can update the elements
  1124. List<Object[]> rows = new ArrayList<Object[]>(inRows);
  1125. ByteBuffer[] rowData = new ByteBuffer[rows.size()];
  1126. for (int i = 0; i < rows.size(); i++) {
  1127. // we need to make sure the row is the right length (fill with null).
  1128. // note, if the row is copied the caller will not be able to access any
  1129. // generated auto-number value, but if they need that info they should
  1130. // use a row array of the right size!
  1131. Object[] row = rows.get(i);
  1132. if(row.length < _columns.size()) {
  1133. row = dupeRow(row, _columns.size());
  1134. // we copied the row, so put the copy back into the rows list
  1135. rows.set(i, row);
  1136. }
  1137. // write the row of data to a temporary buffer
  1138. rowData[i] = createRow(row, getFormat().MAX_ROW_SIZE,
  1139. writeRowBufferH.getPageBuffer(getPageChannel()),
  1140. false, 0);
  1141. if (rowData[i].limit() > getFormat().MAX_ROW_SIZE) {
  1142. throw new IOException("Row size " + rowData[i].limit() +
  1143. " is too large");
  1144. }
  1145. }
  1146. ByteBuffer dataPage = null;
  1147. int pageNumber = PageChannel.INVALID_PAGE_NUMBER;
  1148. for (int i = 0; i < rowData.length; i++) {
  1149. int rowSize = rowData[i].remaining();
  1150. // get page with space
  1151. dataPage = findFreeRowSpace(rowSize, dataPage, pageNumber);
  1152. pageNumber = _addRowBufferH.getPageNumber();
  1153. // write out the row data
  1154. int rowNum = addDataPageRow(dataPage, rowSize, getFormat(), 0);
  1155. dataPage.put(rowData[i]);
  1156. // update the indexes
  1157. RowId rowId = new RowId(pageNumber, rowNum);
  1158. for(Index index : _indexes) {
  1159. index.addRow(rows.get(i), rowId);
  1160. }
  1161. }
  1162. writeDataPage(dataPage, pageNumber);
  1163. // Update tdef page
  1164. updateTableDefinition(rows.size());
  1165. }
  1166. /**
  1167. * Updates the current row to the new values.
  1168. * <p>
  1169. * Note, if this table has an auto-number column(s), the existing value(s)
  1170. * will be maintained, unchanged.
  1171. *
  1172. * @param row new row values for the current row.
  1173. */
  1174. public void updateCurrentRow(Object... row) throws IOException {
  1175. _cursor.updateCurrentRow(row);
  1176. }
  1177. /**
  1178. * Update the row on which the given rowState is currently positioned.
  1179. */
  1180. public void updateRow(RowState rowState, RowId rowId, Object... row)
  1181. throws IOException
  1182. {
  1183. requireValidRowId(rowId);
  1184. // ensure that the relevant row state is up-to-date
  1185. ByteBuffer rowBuffer = positionAtRowData(rowState, rowId);
  1186. int oldRowSize = rowBuffer.remaining();
  1187. requireNonDeletedRow(rowState, rowId);
  1188. // we need to make sure the row is the right length (fill with null).
  1189. if(row.length < _columns.size()) {
  1190. row = dupeRow(row, _columns.size());
  1191. }
  1192. // fill in any auto-numbers (we don't allow autonumber values to be
  1193. // modified) or "keep value" fields
  1194. NullMask nullMask = getRowNullMask(rowBuffer);
  1195. for(Column column : _columns) {
  1196. if(column.isAutoNumber() ||
  1197. (row[column.getColumnIndex()] == Column.KEEP_VALUE)) {
  1198. row[column.getColumnIndex()] = getRowColumn(getFormat(), rowBuffer, nullMask,
  1199. column, rowState);
  1200. }
  1201. }
  1202. // generate new row bytes
  1203. ByteBuffer newRowData = createRow(
  1204. row, getFormat().MAX_ROW_SIZE,
  1205. _singleRowBufferH.getPageBuffer(getPageChannel()), true, oldRowSize);
  1206. if (newRowData.limit() > getFormat().MAX_ROW_SIZE) {
  1207. throw new IOException("Row size " + newRowData.limit() +
  1208. " is too large");
  1209. }
  1210. Object[] oldRowValues = (!_indexes.isEmpty() ?
  1211. rowState.getRowValues() : null);
  1212. // delete old values from indexes
  1213. for(Index index : _indexes) {
  1214. index.deleteRow(oldRowValues, rowId);
  1215. }
  1216. // see if we can squeeze the new row data into the existing row
  1217. rowBuffer.reset();
  1218. int rowSize = newRowData.remaining();
  1219. ByteBuffer dataPage = null;
  1220. int pageNumber = PageChannel.INVALID_PAGE_NUMBER;
  1221. if(oldRowSize >= rowSize) {
  1222. // awesome, slap it in!
  1223. rowBuffer.put(newRowData);
  1224. // grab the page we just updated
  1225. dataPage = rowState.getFinalPage();
  1226. pageNumber = rowState.getFinalRowId().getPageNumber();
  1227. } else {
  1228. // bummer, need to find a new page for the data
  1229. dataPage = findFreeRowSpace(rowSize, null,
  1230. PageChannel.INVALID_PAGE_NUMBER);
  1231. pageNumber = _addRowBufferH.getPageNumber();
  1232. RowId headerRowId = rowState.getHeaderRowId();
  1233. ByteBuffer headerPage = rowState.getHeaderPage();
  1234. if(pageNumber == headerRowId.getPageNumber()) {
  1235. // new row is on the same page as header row, share page
  1236. dataPage = headerPage;
  1237. }
  1238. // write out the new row data (set the deleted flag on the new data row
  1239. // so that it is ignored during normal table traversal)
  1240. int rowNum = addDataPageRow(dataPage, rowSize, getFormat(),
  1241. DELETED_ROW_MASK);
  1242. dataPage.put(newRowData);
  1243. // write the overflow info into the header row and clear out the
  1244. // remaining header data
  1245. rowBuffer = PageChannel.narrowBuffer(
  1246. headerPage,
  1247. findRowStart(headerPage, headerRowId.getRowNumber(), getFormat()),
  1248. findRowEnd(headerPage, headerRowId.getRowNumber(), getFormat()));
  1249. rowBuffer.put((byte)rowNum);
  1250. ByteUtil.put3ByteInt(rowBuffer, pageNumber);
  1251. ByteUtil.clearRemaining(rowBuffer);
  1252. // set the overflow flag on the header row
  1253. int headerRowIndex = getRowStartOffset(headerRowId.getRowNumber(),
  1254. getFormat());
  1255. headerPage.putShort(headerRowIndex,
  1256. (short)(headerPage.getShort(headerRowIndex)
  1257. | OVERFLOW_ROW_MASK));
  1258. if(pageNumber != headerRowId.getPageNumber()) {
  1259. writeDataPage(headerPage, headerRowId.getPageNumber());
  1260. }
  1261. }
  1262. // update the indexes
  1263. for(Index index : _indexes) {
  1264. index.addRow(row, rowId);
  1265. }
  1266. writeDataPage(dataPage, pageNumber);
  1267. updateTableDefinition(0);
  1268. }
  1269. private ByteBuffer findFreeRowSpace(int rowSize, ByteBuffer dataPage,
  1270. int pageNumber)
  1271. throws IOException
  1272. {
  1273. if(dataPage == null) {
  1274. // find last data page (Not bothering to check other pages for free
  1275. // space.)
  1276. UsageMap.PageCursor revPageCursor = _ownedPages.cursor();
  1277. revPageCursor.afterLast();
  1278. while(true) {
  1279. int tmpPageNumber = revPageCursor.getPreviousPage();
  1280. if(tmpPageNumber < 0) {
  1281. break;
  1282. }
  1283. dataPage = _addRowBufferH.setPage(getPageChannel(), tmpPageNumber);
  1284. if(dataPage.get() == PageTypes.DATA) {
  1285. // found last data page, only use if actually listed in free space
  1286. // pages
  1287. if(_freeSpacePages.containsPageNumber(tmpPageNumber)) {
  1288. pageNumber = tmpPageNumber;
  1289. }
  1290. break;
  1291. }
  1292. }
  1293. if(pageNumber == PageChannel.INVALID_PAGE_NUMBER) {
  1294. // No data pages exist (with free space). Create a new one.
  1295. return newDataPage();
  1296. }
  1297. }
  1298. if(!rowFitsOnDataPage(rowSize, dataPage, getFormat())) {
  1299. // Last data page is full. Create a new one.
  1300. writeDataPage(dataPage, pageNumber);
  1301. _freeSpacePages.removePageNumber(pageNumber);
  1302. dataPage = newDataPage();
  1303. }
  1304. return dataPage;
  1305. }
  1306. /**
  1307. * Updates the table definition after rows are modified.
  1308. */
  1309. private void updateTableDefinition(int rowCountInc) throws IOException
  1310. {
  1311. // load table definition
  1312. ByteBuffer tdefPage = _tableDefBufferH.setPage(getPageChannel(),
  1313. _tableDefPageNumber);
  1314. // make sure rowcount and autonumber are up-to-date
  1315. _rowCount += rowCountInc;
  1316. tdefPage.putInt(getFormat().OFFSET_NUM_ROWS, _rowCount);
  1317. tdefPage.putInt(getFormat().OFFSET_NEXT_AUTO_NUMBER, _lastLongAutoNumber);
  1318. // write any index changes
  1319. for (Index index : _indexes) {
  1320. // write the unique entry count for the index to the table definition
  1321. // page
  1322. tdefPage.putInt(index.getUniqueEntryCountOffset(),
  1323. index.getUniqueEntryCount());
  1324. // write the entry page for the index
  1325. index.update();
  1326. }
  1327. // write modified table definition
  1328. getPageChannel().writePage(tdefPage, _tableDefPageNumber);
  1329. }
  1330. /**
  1331. * Create a new data page
  1332. * @return Page number of the new page
  1333. */
  1334. private ByteBuffer newDataPage() throws IOException {
  1335. if (LOG.isDebugEnabled()) {
  1336. LOG.debug("Creating new data page");
  1337. }
  1338. ByteBuffer dataPage = _addRowBufferH.setNewPage(getPageChannel());
  1339. dataPage.put(PageTypes.DATA); //Page type
  1340. dataPage.put((byte) 1); //Unknown
  1341. dataPage.putShort((short)getFormat().PAGE_INITIAL_FREE_SPACE); //Free space in this page
  1342. dataPage.putInt(_tableDefPageNumber); //Page pointer to table definition
  1343. dataPage.putInt(0); //Unknown
  1344. dataPage.putShort((short)0); //Number of rows on this page
  1345. int pageNumber = _addRowBufferH.getPageNumber();
  1346. getPageChannel().writePage(dataPage, pageNumber);
  1347. _ownedPages.addPageNumber(pageNumber);
  1348. _freeSpacePages.addPageNumber(pageNumber);
  1349. return dataPage;
  1350. }
  1351. /**
  1352. * Serialize a row of Objects into a byte buffer.
  1353. * <p>
  1354. * Note, if this table has an auto-number column, the value written will be
  1355. * put back into the given row array.
  1356. *
  1357. * @param rowArray row data, expected to be correct length for this table
  1358. * @param maxRowSize max size the data can be for this row
  1359. * @param buffer buffer to which to write the row data
  1360. * @return the given buffer, filled with the row data
  1361. */
  1362. ByteBuffer createRow(Object[] rowArray, int maxRowSize, ByteBuffer buffer,
  1363. boolean isUpdate, int minRowSize)
  1364. throws IOException
  1365. {
  1366. buffer.putShort(_maxColumnCount);
  1367. NullMask nullMask = new NullMask(_maxColumnCount);
  1368. //Fixed length column data comes first
  1369. int fixedDataStart = buffer.position();
  1370. int fixedDataEnd = fixedDataStart;
  1371. for (Column col : _columns) {
  1372. if(!col.isVariableLength()) {
  1373. Object rowValue = rowArray[col.getColumnIndex()];
  1374. if (col.getType() == DataType.BOOLEAN) {
  1375. if(Column.toBooleanValue(rowValue)) {
  1376. //Booleans are stored in the null mask
  1377. nullMask.markNotNull(col);
  1378. }
  1379. } else {
  1380. if(col.isAutoNumber() && !isUpdate) {
  1381. // ignore given row value, use next autonumber
  1382. rowValue = col.getAutoNumberGenerator().getNext();
  1383. // we need to stick this back in the row so that the indexes get
  1384. // updated correctly (and caller can get the generated value)
  1385. rowArray[col.getColumnIndex()] = rowValue;
  1386. }
  1387. if(rowValue != null) {
  1388. // we have a value
  1389. nullMask.markNotNull(col);
  1390. //remainingRowLength is ignored when writing fixed length data
  1391. buffer.position(fixedDataStart + col.getFixedDataOffset());
  1392. buffer.put(col.write(rowValue, 0));
  1393. // keep track of the end of fixed data
  1394. if(buffer.position() > fixedDataEnd) {
  1395. fixedDataEnd = buffer.position();
  1396. }
  1397. }
  1398. }
  1399. }
  1400. }
  1401. // reposition at end of fixed data
  1402. buffer.position(fixedDataEnd);
  1403. // only need this info if this table contains any var length data
  1404. if(_maxVarColumnCount > 0) {
  1405. // figure out how much space remains for var length data. first,
  1406. // account for already written space
  1407. maxRowSize -= buffer.position();
  1408. // now, account for trailer space
  1409. int trailerSize = (nullMask.byteSize() + 4 + (_maxVarColumnCount * 2));
  1410. maxRowSize -= trailerSize;
  1411. // for each non-null long value column we need to reserve a small
  1412. // amount of space so that we don't end up running out of row space
  1413. // later by being too greedy
  1414. for (Column varCol : _varColumns) {
  1415. if((varCol.getType().isLongValue()) &&
  1416. (rowArray[varCol.getColumnIndex()] != null)) {
  1417. maxRowSize -= getFormat().SIZE_LONG_VALUE_DEF;
  1418. }
  1419. }
  1420. //Now write out variable length column data
  1421. short[] varColumnOffsets = new short[_maxVarColumnCount];
  1422. int varColumnOffsetsIndex = 0;
  1423. for (Column varCol : _varColumns) {
  1424. short offset = (short) buffer.position();
  1425. Object rowValue = rowArray[varCol.getColumnIndex()];
  1426. if (rowValue != null) {
  1427. // we have a value
  1428. nullMask.markNotNull(varCol);
  1429. ByteBuffer varDataBuf = varCol.write(rowValue, maxRowSize);
  1430. maxRowSize -= varDataBuf.remaining();
  1431. if(varCol.getType().isLongValue()) {
  1432. // we already accounted for some amount of the long value data
  1433. // above. add that space back so we don't double count
  1434. maxRowSize += getFormat().SIZE_LONG_VALUE_DEF;
  1435. }
  1436. buffer.put(varDataBuf);
  1437. }
  1438. // we do a loop here so that we fill in offsets for deleted columns
  1439. while(varColumnOffsetsIndex <= varCol.getVarLenTableIndex()) {
  1440. varColumnOffsets[varColumnOffsetsIndex++] = offset;
  1441. }
  1442. }
  1443. // fill in offsets for any remaining deleted columns
  1444. while(varColumnOffsetsIndex < varColumnOffsets.length) {
  1445. varColumnOffsets[varColumnOffsetsIndex++] = (short) buffer.position();
  1446. }
  1447. // record where we stopped writing
  1448. int eod = buffer.position();
  1449. // insert padding if necessary
  1450. padRowBuffer(buffer, minRowSize, trailerSize);
  1451. buffer.putShort((short) eod); //EOD marker
  1452. //Now write out variable length offsets
  1453. //Offsets are stored in reverse order
  1454. for (int i = _maxVarColumnCount - 1; i >= 0; i--) {
  1455. buffer.putShort(varColumnOffsets[i]);
  1456. }
  1457. buffer.putShort(_maxVarColumnCount); //Number of var length columns
  1458. } else {
  1459. // insert padding for row w/ no var cols
  1460. padRowBuffer(buffer, minRowSize, nullMask.byteSize());
  1461. }
  1462. nullMask.write(buffer); //Null mask
  1463. buffer.flip();
  1464. if (LOG.isDebugEnabled()) {
  1465. LOG.debug("Creating new data block:\n" + ByteUtil.toHexString(buffer, buffer.limit()));
  1466. }
  1467. return buffer;
  1468. }
  1469. private void padRowBuffer(ByteBuffer buffer, int minRowSize, int trailerSize)
  1470. {
  1471. int pos = buffer.position();
  1472. if((pos + trailerSize) < minRowSize) {
  1473. // pad the row to get to the min byte size
  1474. int padSize = minRowSize - (pos + trailerSize);
  1475. ByteUtil.clearRange(buffer, pos, pos + padSize);
  1476. ByteUtil.forward(buffer, padSize);
  1477. }
  1478. }
  1479. public int getRowCount() {
  1480. return _rowCount;
  1481. }
  1482. int getNextLongAutoNumber() {
  1483. // note, the saved value is the last one handed out, so pre-increment
  1484. return ++_lastLongAutoNumber;
  1485. }
  1486. int getLastLongAutoNumber() {
  1487. // gets the last used auto number (does not modify)
  1488. return _lastLongAutoNumber;
  1489. }
  1490. @Override
  1491. public String toString() {
  1492. StringBuilder rtn = new StringBuilder();
  1493. rtn.append("Type: " + _tableType);
  1494. rtn.append("\nName: " + _name);
  1495. rtn.append("\nRow count: " + _rowCount);
  1496. rtn.append("\nColumn count: " + _columns.size());
  1497. rtn.append("\nIndex count: " + _indexCount);
  1498. rtn.append("\nColumns:\n");
  1499. for(Column col : _columns) {
  1500. rtn.append(col);
  1501. }
  1502. rtn.append("\nIndexes:\n");
  1503. for(Index index : _indexes) {
  1504. rtn.append(index);
  1505. }
  1506. rtn.append("\nOwned pages: " + _ownedPages + "\n");
  1507. return rtn.toString();
  1508. }
  1509. /**
  1510. * @return A simple String representation of the entire table in tab-delimited format
  1511. */
  1512. public String display() throws IOException {
  1513. return display(Long.MAX_VALUE);
  1514. }
  1515. /**
  1516. * @param limit Maximum number of rows to display
  1517. * @return A simple String representation of the entire table in tab-delimited format
  1518. */
  1519. public String display(long limit) throws IOException {
  1520. reset();
  1521. StringBuilder rtn = new StringBuilder();
  1522. for(Iterator<Column> iter = _columns.iterator(); iter.hasNext(); ) {
  1523. Column col = iter.next();
  1524. rtn.append(col.getName());
  1525. if (iter.hasNext()) {
  1526. rtn.append("\t");
  1527. }
  1528. }
  1529. rtn.append("\n");
  1530. Map<String, Object> row;
  1531. int rowCount = 0;
  1532. while ((rowCount++ < limit) && (row = getNextRow()) != null) {
  1533. for(Iterator<Object> iter = row.values().iterator(); iter.hasNext(); ) {
  1534. Object obj = iter.next();
  1535. if (obj instanceof byte[]) {
  1536. byte[] b = (byte[]) obj;
  1537. rtn.append(ByteUtil.toHexString(b));
  1538. //This block can be used to easily dump a binary column to a file
  1539. /*java.io.File f = java.io.File.createTempFile("ole", ".bin");
  1540. java.io.FileOutputStream out = new java.io.FileOutputStream(f);
  1541. out.write(b);
  1542. out.flush();
  1543. out.close();*/
  1544. } else {
  1545. rtn.append(String.valueOf(obj));
  1546. }
  1547. if (iter.hasNext()) {
  1548. rtn.append("\t");
  1549. }
  1550. }
  1551. rtn.append("\n");
  1552. }
  1553. return rtn.toString();
  1554. }
  1555. /**
  1556. * Updates free space and row info for a new row of the given size in the
  1557. * given data page. Positions the page for writing the row data.
  1558. * @return the row number of the new row
  1559. */
  1560. public static int addDataPageRow(ByteBuffer dataPage,
  1561. int rowSize,
  1562. JetFormat format,
  1563. int rowFlags)
  1564. {
  1565. int rowSpaceUsage = getRowSpaceUsage(rowSize, format);
  1566. // Decrease free space record.
  1567. short freeSpaceInPage = dataPage.getShort(format.OFFSET_FREE_SPACE);
  1568. dataPage.putShort(format.OFFSET_FREE_SPACE, (short) (freeSpaceInPage -
  1569. rowSpaceUsage));
  1570. // Increment row count record.
  1571. short rowCount = dataPage.getShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE);
  1572. dataPage.putShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE,
  1573. (short) (rowCount + 1));
  1574. // determine row position
  1575. short rowLocation = findRowEnd(dataPage, rowCount, format);
  1576. rowLocation -= rowSize;
  1577. // write row position
  1578. dataPage.putShort(getRowStartOffset(rowCount, format),
  1579. (short)(rowLocation | rowFlags));
  1580. // set position for row data
  1581. dataPage.position(rowLocation);
  1582. return rowCount;
  1583. }
  1584. /**
  1585. * Returns the row count for the current page. If the page is invalid
  1586. * ({@code null}) or the page is not a DATA page, 0 is returned.
  1587. */
  1588. private static int getRowsOnDataPage(ByteBuffer rowBuffer, JetFormat format)
  1589. throws IOException
  1590. {
  1591. int rowsOnPage = 0;
  1592. if((rowBuffer != null) && (rowBuffer.get(0) == PageTypes.DATA)) {
  1593. rowsOnPage = rowBuffer.getShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE);
  1594. }
  1595. return rowsOnPage;
  1596. }
  1597. /**
  1598. * @throws IllegalStateException if the given rowId is invalid
  1599. */
  1600. private static void requireValidRowId(RowId rowId) {
  1601. if(!rowId.isValid()) {
  1602. throw new IllegalArgumentException("Given rowId is invalid: " + rowId);
  1603. }
  1604. }
  1605. /**
  1606. * @throws IllegalStateException if the given row is invalid or deleted
  1607. */
  1608. private static void requireNonDeletedRow(RowState rowState, RowId rowId) {
  1609. if(!rowState.isValid()) {
  1610. throw new IllegalArgumentException(
  1611. "Given rowId is invalid for this table: " + rowId);
  1612. }
  1613. if(rowState.isDeleted()) {
  1614. throw new IllegalStateException("Row is deleted: " + rowId);
  1615. }
  1616. }
  1617. public static boolean isDeletedRow(short rowStart) {
  1618. return ((rowStart & DELETED_ROW_MASK) != 0);
  1619. }
  1620. public static boolean isOverflowRow(short rowStart) {
  1621. return ((rowStart & OVERFLOW_ROW_MASK) != 0);
  1622. }
  1623. public static short cleanRowStart(short rowStart) {
  1624. return (short)(rowStart & OFFSET_MASK);
  1625. }
  1626. public static short findRowStart(ByteBuffer buffer, int rowNum,
  1627. JetFormat format)
  1628. {
  1629. return cleanRowStart(
  1630. buffer.getShort(getRowStartOffset(rowNum, format)));
  1631. }
  1632. public static int getRowStartOffset(int rowNum, JetFormat format)
  1633. {
  1634. return format.OFFSET_ROW_START + (format.SIZE_ROW_LOCATION * rowNum);
  1635. }
  1636. public static short findRowEnd(ByteBuffer buffer, int rowNum,
  1637. JetFormat format)
  1638. {
  1639. return (short)((rowNum == 0) ?
  1640. format.PAGE_SIZE :
  1641. cleanRowStart(
  1642. buffer.getShort(getRowEndOffset(rowNum, format))));
  1643. }
  1644. public static int getRowEndOffset(int rowNum, JetFormat format)
  1645. {
  1646. return format.OFFSET_ROW_START + (format.SIZE_ROW_LOCATION * (rowNum - 1));
  1647. }
  1648. public static int getRowSpaceUsage(int rowSize, JetFormat format)
  1649. {
  1650. return rowSize + format.SIZE_ROW_LOCATION;
  1651. }
  1652. /**
  1653. * @return the "AutoNumber" columns in the given collection of columns.
  1654. */
  1655. public static List<Column> getAutoNumberColumns(Collection<Column> columns) {
  1656. List<Column> autoCols = new ArrayList<Column>();
  1657. for(Column c : columns) {
  1658. if(c.isAutoNumber()) {
  1659. autoCols.add(c);
  1660. }
  1661. }
  1662. return autoCols;
  1663. }
  1664. /**
  1665. * Returns {@code true} if a row of the given size will fit on the given
  1666. * data page, {@code false} otherwise.
  1667. */
  1668. public static boolean rowFitsOnDataPage(
  1669. int rowLength, ByteBuffer dataPage, JetFormat format)
  1670. throws IOException
  1671. {
  1672. int rowSpaceUsage = getRowSpaceUsage(rowLength, format);
  1673. short freeSpaceInPage = dataPage.getShort(format.OFFSET_FREE_SPACE);
  1674. int rowsOnPage = getRowsOnDataPage(dataPage, format);
  1675. return ((rowSpaceUsage <= freeSpaceInPage) &&
  1676. (rowsOnPage < format.MAX_NUM_ROWS_ON_DATA_PAGE));
  1677. }
  1678. /**
  1679. * Duplicates and returns a row of data, optionally with a longer length
  1680. * filled with {@code null}.
  1681. */
  1682. static Object[] dupeRow(Object[] row, int newRowLength) {
  1683. Object[] copy = new Object[newRowLength];
  1684. System.arraycopy(row, 0, copy, 0, row.length);
  1685. return copy;
  1686. }
  1687. /** various statuses for the row data */
  1688. private enum RowStatus {
  1689. INIT, INVALID_PAGE, INVALID_ROW, VALID, DELETED, NORMAL, OVERFLOW;
  1690. }
  1691. /** the phases the RowState moves through as the data is parsed */
  1692. private enum RowStateStatus {
  1693. INIT, AT_HEADER, AT_FINAL;
  1694. }
  1695. /**
  1696. * Maintains the state of reading a row of data.
  1697. */
  1698. public final class RowState
  1699. {
  1700. /** Buffer used for reading the header row data pages */
  1701. private final TempPageHolder _headerRowBufferH;
  1702. /** the header rowId */
  1703. private RowId _headerRowId = RowId.FIRST_ROW_ID;
  1704. /** the number of rows on the header page */
  1705. private int _rowsOnHeaderPage;
  1706. /** the rowState status */
  1707. private RowStateStatus _status = RowStateStatus.INIT;
  1708. /** the row status */
  1709. private RowStatus _rowStatus = RowStatus.INIT;
  1710. /** buffer used for reading overflow pages */
  1711. private final TempPageHolder _overflowRowBufferH =
  1712. TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
  1713. /** the row buffer which contains the final data (after following any
  1714. overflow pointers) */
  1715. private ByteBuffer _finalRowBuffer;
  1716. /** the rowId which contains the final data (after following any overflow
  1717. pointers) */
  1718. private RowId _finalRowId = null;
  1719. /** true if the row values array has data */
  1720. private boolean _haveRowValues;
  1721. /** values read from the last row */
  1722. private final Object[] _rowValues;
  1723. /** last modification count seen on the table we track this so that the
  1724. rowState can detect updates to the table and re-read any buffered
  1725. data */
  1726. private int _lastModCount;
  1727. /** optional error handler to use when row errors are encountered */
  1728. private ErrorHandler _errorHandler;
  1729. /** cached variable column offsets for jump-table based rows */
  1730. private short[] _varColOffsets;
  1731. private RowState(TempBufferHolder.Type headerType) {
  1732. _headerRowBufferH = TempPageHolder.newHolder(headerType);
  1733. _rowValues = new Object[Table.this.getColumnCount()];
  1734. _lastModCount = Table.this._modCount;
  1735. }
  1736. public Table getTable() {
  1737. return Table.this;
  1738. }
  1739. public ErrorHandler getErrorHandler() {
  1740. return((_errorHandler != null) ? _errorHandler :
  1741. getTable().getErrorHandler());
  1742. }
  1743. public void setErrorHandler(ErrorHandler newErrorHandler) {
  1744. _errorHandler = newErrorHandler;
  1745. }
  1746. public void reset() {
  1747. _finalRowId = null;
  1748. _finalRowBuffer = null;
  1749. _rowsOnHeaderPage = 0;
  1750. _status = RowStateStatus.INIT;
  1751. _rowStatus = RowStatus.INIT;
  1752. _varColOffsets = null;
  1753. if(_haveRowValues) {
  1754. Arrays.fill(_rowValues, null);
  1755. _haveRowValues = false;
  1756. }
  1757. }
  1758. public boolean isUpToDate() {
  1759. return(Table.this._modCount == _lastModCount);
  1760. }
  1761. private void checkForModification() {
  1762. if(!isUpToDate()) {
  1763. reset();
  1764. _headerRowBufferH.invalidate();
  1765. _overflowRowBufferH.invalidate();
  1766. _lastModCount = Table.this._modCount;
  1767. }
  1768. }
  1769. private ByteBuffer getFinalPage()
  1770. throws IOException
  1771. {
  1772. if(_finalRowBuffer == null) {
  1773. // (re)load current page
  1774. _finalRowBuffer = getHeaderPage();
  1775. }
  1776. return _finalRowBuffer;
  1777. }
  1778. public RowId getFinalRowId() {
  1779. if(_finalRowId == null) {
  1780. _finalRowId = getHeaderRowId();
  1781. }
  1782. return _finalRowId;
  1783. }
  1784. private void setRowStatus(RowStatus rowStatus) {
  1785. _rowStatus = rowStatus;
  1786. }
  1787. public boolean isValid() {
  1788. return(_rowStatus.ordinal() >= RowStatus.VALID.ordinal());
  1789. }
  1790. public boolean isDeleted() {
  1791. return(_rowStatus == RowStatus.DELETED);
  1792. }
  1793. public boolean isOverflow() {
  1794. return(_rowStatus == RowStatus.OVERFLOW);
  1795. }
  1796. public boolean isHeaderPageNumberValid() {
  1797. return(_rowStatus.ordinal() > RowStatus.INVALID_PAGE.ordinal());
  1798. }
  1799. public boolean isHeaderRowNumberValid() {
  1800. return(_rowStatus.ordinal() > RowStatus.INVALID_ROW.ordinal());
  1801. }
  1802. private void setStatus(RowStateStatus status) {
  1803. _status = status;
  1804. }
  1805. public boolean isAtHeaderRow() {
  1806. return(_status.ordinal() >= RowStateStatus.AT_HEADER.ordinal());
  1807. }
  1808. public boolean isAtFinalRow() {
  1809. return(_status.ordinal() >= RowStateStatus.AT_FINAL.ordinal());
  1810. }
  1811. private Object setRowValue(int idx, Object value) {
  1812. _haveRowValues = true;
  1813. _rowValues[idx] = value;
  1814. return value;
  1815. }
  1816. public Object[] getRowValues() {
  1817. return dupeRow(_rowValues, _rowValues.length);
  1818. }
  1819. private short[] getVarColOffsets() {
  1820. return _varColOffsets;
  1821. }
  1822. private void setVarColOffsets(short[] varColOffsets) {
  1823. _varColOffsets = varColOffsets;
  1824. }
  1825. public RowId getHeaderRowId() {
  1826. return _headerRowId;
  1827. }
  1828. public int getRowsOnHeaderPage() {
  1829. return _rowsOnHeaderPage;
  1830. }
  1831. private ByteBuffer getHeaderPage()
  1832. throws IOException
  1833. {
  1834. checkForModification();
  1835. return _headerRowBufferH.getPage(getPageChannel());
  1836. }
  1837. private ByteBuffer setHeaderRow(RowId rowId)
  1838. throws IOException
  1839. {
  1840. checkForModification();
  1841. // don't do any work if we are already positioned correctly
  1842. if(isAtHeaderRow() && (getHeaderRowId().equals(rowId))) {
  1843. return(isValid() ? getHeaderPage() : null);
  1844. }
  1845. // rejigger everything
  1846. reset();
  1847. _headerRowId = rowId;
  1848. _finalRowId = rowId;
  1849. int pageNumber = rowId.getPageNumber();
  1850. int rowNumber = rowId.getRowNumber();
  1851. if((pageNumber < 0) || !_ownedPages.containsPageNumber(pageNumber)) {
  1852. setRowStatus(RowStatus.INVALID_PAGE);
  1853. return null;
  1854. }
  1855. _finalRowBuffer = _headerRowBufferH.setPage(getPageChannel(),
  1856. pageNumber);
  1857. _rowsOnHeaderPage = getRowsOnDataPage(_finalRowBuffer, getFormat());
  1858. if((rowNumber < 0) || (rowNumber >= _rowsOnHeaderPage)) {
  1859. setRowStatus(RowStatus.INVALID_ROW);
  1860. return null;
  1861. }
  1862. setRowStatus(RowStatus.VALID);
  1863. return _finalRowBuffer;
  1864. }
  1865. private ByteBuffer setOverflowRow(RowId rowId)
  1866. throws IOException
  1867. {
  1868. // this should never see modifications because it only happens within
  1869. // the positionAtRowData method
  1870. if(!isUpToDate()) {
  1871. throw new IllegalStateException("Table modified while searching?");
  1872. }
  1873. if(_rowStatus != RowStatus.OVERFLOW) {
  1874. throw new IllegalStateException("Row is not an overflow row?");
  1875. }
  1876. _finalRowId = rowId;
  1877. _finalRowBuffer = _overflowRowBufferH.setPage(getPageChannel(),
  1878. rowId.getPageNumber());
  1879. return _finalRowBuffer;
  1880. }
  1881. private Object handleRowError(Column column,
  1882. byte[] columnData,
  1883. Exception error)
  1884. throws IOException
  1885. {
  1886. return getErrorHandler().handleRowError(column, columnData,
  1887. this, error);
  1888. }
  1889. @Override
  1890. public String toString()
  1891. {
  1892. return "RowState: headerRowId = " + _headerRowId + ", finalRowId = " +
  1893. _finalRowId;
  1894. }
  1895. }
  1896. }