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.

TableImpl.java 88KB

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