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

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694
  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.BufferOverflowException;
  18. import java.nio.ByteBuffer;
  19. import java.nio.charset.Charset;
  20. import java.time.LocalDateTime;
  21. import java.util.AbstractMap;
  22. import java.util.ArrayList;
  23. import java.util.Arrays;
  24. import java.util.Collection;
  25. import java.util.Collections;
  26. import java.util.Comparator;
  27. import java.util.HashMap;
  28. import java.util.Iterator;
  29. import java.util.LinkedHashSet;
  30. import java.util.List;
  31. import java.util.Map;
  32. import java.util.Set;
  33. import java.util.TreeSet;
  34. import com.healthmarketscience.jackcess.BatchUpdateException;
  35. import com.healthmarketscience.jackcess.Column;
  36. import com.healthmarketscience.jackcess.ColumnBuilder;
  37. import com.healthmarketscience.jackcess.ConstraintViolationException;
  38. import com.healthmarketscience.jackcess.CursorBuilder;
  39. import com.healthmarketscience.jackcess.Index;
  40. import com.healthmarketscience.jackcess.IndexBuilder;
  41. import com.healthmarketscience.jackcess.InvalidValueException;
  42. import com.healthmarketscience.jackcess.JackcessException;
  43. import com.healthmarketscience.jackcess.PropertyMap;
  44. import com.healthmarketscience.jackcess.Row;
  45. import com.healthmarketscience.jackcess.RowId;
  46. import com.healthmarketscience.jackcess.Table;
  47. import com.healthmarketscience.jackcess.expr.Identifier;
  48. import com.healthmarketscience.jackcess.util.ErrorHandler;
  49. import com.healthmarketscience.jackcess.util.ExportUtil;
  50. import org.apache.commons.lang3.builder.ToStringBuilder;
  51. import org.apache.commons.logging.Log;
  52. import org.apache.commons.logging.LogFactory;
  53. /**
  54. * A single database table
  55. * <p>
  56. * Is not thread-safe.
  57. *
  58. * @author Tim McCune
  59. * @usage _intermediate_class_
  60. */
  61. public class TableImpl implements Table, PropertyMaps.Owner
  62. {
  63. private static final Log LOG = LogFactory.getLog(TableImpl.class);
  64. private static final short OFFSET_MASK = (short)0x1FFF;
  65. private static final short DELETED_ROW_MASK = (short)0x8000;
  66. private static final short OVERFLOW_ROW_MASK = (short)0x4000;
  67. static final int MAGIC_TABLE_NUMBER = 1625;
  68. private static final int MAX_BYTE = 256;
  69. /**
  70. * Table type code for system tables
  71. * @usage _intermediate_class_
  72. */
  73. public static final byte TYPE_SYSTEM = 0x53;
  74. /**
  75. * Table type code for user tables
  76. * @usage _intermediate_class_
  77. */
  78. public static final byte TYPE_USER = 0x4e;
  79. public enum IndexFeature {
  80. EXACT_MATCH, EXACT_UNIQUE_ONLY, ANY_MATCH;
  81. }
  82. /** comparator which sorts variable length columns based on their index into
  83. the variable length offset table */
  84. private static final Comparator<ColumnImpl> VAR_LEN_COLUMN_COMPARATOR =
  85. new Comparator<ColumnImpl>() {
  86. @Override
  87. public int compare(ColumnImpl c1, ColumnImpl c2) {
  88. return ((c1.getVarLenTableIndex() < c2.getVarLenTableIndex()) ? -1 :
  89. ((c1.getVarLenTableIndex() > c2.getVarLenTableIndex()) ? 1 :
  90. 0));
  91. }
  92. };
  93. /** comparator which sorts columns based on their display index */
  94. private static final Comparator<ColumnImpl> DISPLAY_ORDER_COMPARATOR =
  95. new Comparator<ColumnImpl>() {
  96. @Override
  97. public int compare(ColumnImpl c1, ColumnImpl c2) {
  98. return ((c1.getDisplayIndex() < c2.getDisplayIndex()) ? -1 :
  99. ((c1.getDisplayIndex() > c2.getDisplayIndex()) ? 1 :
  100. 0));
  101. }
  102. };
  103. /** owning database */
  104. private final DatabaseImpl _database;
  105. /** additional table flags from the catalog entry */
  106. private final int _flags;
  107. /** Type of the table (either TYPE_SYSTEM or TYPE_USER) */
  108. private final byte _tableType;
  109. /** Number of actual indexes on the table */
  110. private int _indexCount;
  111. /** Number of logical indexes for the table */
  112. private int _logicalIndexCount;
  113. /** page number of the definition of this table */
  114. private final int _tableDefPageNumber;
  115. /** max Number of columns in the table (includes previous deletions) */
  116. private short _maxColumnCount;
  117. /** max Number of variable columns in the table */
  118. private short _maxVarColumnCount;
  119. /** List of columns in this table, ordered by column number */
  120. private final List<ColumnImpl> _columns = new ArrayList<ColumnImpl>();
  121. /** List of variable length columns in this table, ordered by offset */
  122. private final List<ColumnImpl> _varColumns = new ArrayList<ColumnImpl>();
  123. /** List of autonumber columns in this table, ordered by column number */
  124. private final List<ColumnImpl> _autoNumColumns = new ArrayList<ColumnImpl>(1);
  125. /** handler for calculated columns */
  126. private final CalcColEvaluator _calcColEval = new CalcColEvaluator();
  127. /** List of indexes on this table (multiple logical indexes may be backed by
  128. the same index data) */
  129. private final List<IndexImpl> _indexes = new ArrayList<IndexImpl>();
  130. /** List of index datas on this table (the actual backing data for an
  131. index) */
  132. private final List<IndexData> _indexDatas = new ArrayList<IndexData>();
  133. /** List of columns in this table which are in one or more indexes */
  134. private final Set<ColumnImpl> _indexColumns = new LinkedHashSet<ColumnImpl>();
  135. /** Table name as stored in Database */
  136. private final String _name;
  137. /** Usage map of pages that this table owns */
  138. private final UsageMap _ownedPages;
  139. /** Usage map of pages that this table owns with free space on them */
  140. private final UsageMap _freeSpacePages;
  141. /** Number of rows in the table */
  142. private int _rowCount;
  143. /** last long auto number for the table */
  144. private int _lastLongAutoNumber;
  145. /** last complex type auto number for the table */
  146. private int _lastComplexTypeAutoNumber;
  147. /** modification count for the table, keeps row-states up-to-date */
  148. private int _modCount;
  149. /** page buffer used to update data pages when adding rows */
  150. private final TempPageHolder _addRowBufferH =
  151. TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
  152. /** page buffer used to update the table def page */
  153. private final TempPageHolder _tableDefBufferH =
  154. TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
  155. /** buffer used to writing rows of data */
  156. private final TempBufferHolder _writeRowBufferH =
  157. TempBufferHolder.newHolder(TempBufferHolder.Type.SOFT, true);
  158. /** page buffer used to write out-of-row "long value" data */
  159. private final TempPageHolder _longValueBufferH =
  160. TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
  161. /** optional error handler to use when row errors are encountered */
  162. private ErrorHandler _tableErrorHandler;
  163. /** properties for this table */
  164. private PropertyMap _props;
  165. /** properties group for this table (and columns) */
  166. private PropertyMaps _propertyMaps;
  167. /** optional flag indicating whether or not auto numbers can be directly
  168. inserted by the user */
  169. private Boolean _allowAutoNumInsert;
  170. /** foreign-key enforcer for this table */
  171. private final FKEnforcer _fkEnforcer;
  172. /** table validator if any (and enabled) */
  173. private RowValidatorEvalContext _rowValidator;
  174. /** default cursor for iterating through the table, kept here for basic
  175. table traversal */
  176. private CursorImpl _defaultCursor;
  177. /**
  178. * Only used by unit tests
  179. * @usage _advanced_method_
  180. */
  181. protected TableImpl(boolean testing, List<ColumnImpl> columns)
  182. throws IOException
  183. {
  184. if(!testing) {
  185. throw new IllegalArgumentException();
  186. }
  187. _database = null;
  188. _tableDefPageNumber = PageChannel.INVALID_PAGE_NUMBER;
  189. _name = null;
  190. _columns.addAll(columns);
  191. for(ColumnImpl col : _columns) {
  192. if(col.getType().isVariableLength()) {
  193. _varColumns.add(col);
  194. }
  195. }
  196. _maxColumnCount = (short)_columns.size();
  197. _maxVarColumnCount = (short)_varColumns.size();
  198. initAutoNumberColumns();
  199. _fkEnforcer = null;
  200. _flags = 0;
  201. _tableType = TYPE_USER;
  202. _indexCount = 0;
  203. _logicalIndexCount = 0;
  204. _ownedPages = null;
  205. _freeSpacePages = null;
  206. }
  207. /**
  208. * @param database database which owns this table
  209. * @param tableBuffer Buffer to read the table with
  210. * @param pageNumber Page number of the table definition
  211. * @param name Table name
  212. */
  213. protected TableImpl(DatabaseImpl database, ByteBuffer tableBuffer,
  214. int pageNumber, String name, int flags)
  215. throws IOException
  216. {
  217. _database = database;
  218. _tableDefPageNumber = pageNumber;
  219. _name = name;
  220. _flags = flags;
  221. // read table definition
  222. tableBuffer = loadCompleteTableDefinitionBuffer(tableBuffer, null);
  223. _rowCount = tableBuffer.getInt(getFormat().OFFSET_NUM_ROWS);
  224. _lastLongAutoNumber = tableBuffer.getInt(getFormat().OFFSET_NEXT_AUTO_NUMBER);
  225. if(getFormat().OFFSET_NEXT_COMPLEX_AUTO_NUMBER >= 0) {
  226. _lastComplexTypeAutoNumber = tableBuffer.getInt(
  227. getFormat().OFFSET_NEXT_COMPLEX_AUTO_NUMBER);
  228. }
  229. _tableType = tableBuffer.get(getFormat().OFFSET_TABLE_TYPE);
  230. _maxColumnCount = tableBuffer.getShort(getFormat().OFFSET_MAX_COLS);
  231. _maxVarColumnCount = tableBuffer.getShort(getFormat().OFFSET_NUM_VAR_COLS);
  232. short columnCount = tableBuffer.getShort(getFormat().OFFSET_NUM_COLS);
  233. _logicalIndexCount = tableBuffer.getInt(getFormat().OFFSET_NUM_INDEX_SLOTS);
  234. _indexCount = tableBuffer.getInt(getFormat().OFFSET_NUM_INDEXES);
  235. tableBuffer.position(getFormat().OFFSET_OWNED_PAGES);
  236. _ownedPages = UsageMap.read(getDatabase(), tableBuffer);
  237. tableBuffer.position(getFormat().OFFSET_FREE_SPACE_PAGES);
  238. _freeSpacePages = UsageMap.read(getDatabase(), tableBuffer);
  239. for (int i = 0; i < _indexCount; i++) {
  240. _indexDatas.add(IndexData.create(this, tableBuffer, i, getFormat()));
  241. }
  242. readColumnDefinitions(tableBuffer, columnCount);
  243. readIndexDefinitions(tableBuffer);
  244. // read column usage map info
  245. while((tableBuffer.remaining() >= 2) &&
  246. readColumnUsageMaps(tableBuffer)) {
  247. // keep reading ...
  248. }
  249. // re-sort columns if necessary
  250. if(getDatabase().getColumnOrder() != ColumnOrder.DATA) {
  251. Collections.sort(_columns, DISPLAY_ORDER_COMPARATOR);
  252. }
  253. for(ColumnImpl col : _columns) {
  254. // some columns need to do extra work after the table is completely
  255. // loaded
  256. col.postTableLoadInit();
  257. }
  258. _fkEnforcer = new FKEnforcer(this);
  259. if(!isSystem()) {
  260. // after fully constructed, allow column/row validators to be configured
  261. // (but only for user tables)
  262. for(ColumnImpl col : _columns) {
  263. col.initColumnValidator();
  264. }
  265. reloadRowValidator();
  266. }
  267. }
  268. private void reloadRowValidator() throws IOException {
  269. // reset table row validator before proceeding
  270. _rowValidator = null;
  271. if(!getDatabase().isEvaluateExpressions()) {
  272. return;
  273. }
  274. PropertyMap props = getProperties();
  275. String exprStr = PropertyMaps.getTrimmedStringProperty(
  276. props, PropertyMap.VALIDATION_RULE_PROP);
  277. if(exprStr != null) {
  278. String helpStr = PropertyMaps.getTrimmedStringProperty(
  279. props, PropertyMap.VALIDATION_TEXT_PROP);
  280. _rowValidator = new RowValidatorEvalContext(this)
  281. .setExpr(exprStr, helpStr);
  282. }
  283. }
  284. @Override
  285. public String getName() {
  286. return _name;
  287. }
  288. @Override
  289. public boolean isHidden() {
  290. return((_flags & DatabaseImpl.HIDDEN_OBJECT_FLAG) != 0);
  291. }
  292. @Override
  293. public boolean isSystem() {
  294. return(_tableType != TYPE_USER);
  295. }
  296. /**
  297. * @usage _advanced_method_
  298. */
  299. public int getMaxColumnCount() {
  300. return _maxColumnCount;
  301. }
  302. @Override
  303. public int getColumnCount() {
  304. return _columns.size();
  305. }
  306. @Override
  307. public DatabaseImpl getDatabase() {
  308. return _database;
  309. }
  310. /**
  311. * @usage _advanced_method_
  312. */
  313. public JetFormat getFormat() {
  314. return getDatabase().getFormat();
  315. }
  316. /**
  317. * @usage _advanced_method_
  318. */
  319. public PageChannel getPageChannel() {
  320. return getDatabase().getPageChannel();
  321. }
  322. @Override
  323. public ErrorHandler getErrorHandler() {
  324. return((_tableErrorHandler != null) ? _tableErrorHandler :
  325. getDatabase().getErrorHandler());
  326. }
  327. @Override
  328. public void setErrorHandler(ErrorHandler newErrorHandler) {
  329. _tableErrorHandler = newErrorHandler;
  330. }
  331. public int getTableDefPageNumber() {
  332. return _tableDefPageNumber;
  333. }
  334. @Override
  335. public boolean isAllowAutoNumberInsert() {
  336. return ((_allowAutoNumInsert != null) ? (boolean)_allowAutoNumInsert :
  337. getDatabase().isAllowAutoNumberInsert());
  338. }
  339. @Override
  340. public void setAllowAutoNumberInsert(Boolean allowAutoNumInsert) {
  341. _allowAutoNumInsert = allowAutoNumInsert;
  342. }
  343. /**
  344. * @usage _advanced_method_
  345. */
  346. public RowState createRowState() {
  347. return new RowState(TempBufferHolder.Type.HARD);
  348. }
  349. /**
  350. * @usage _advanced_method_
  351. */
  352. public UsageMap.PageCursor getOwnedPagesCursor() {
  353. return _ownedPages.cursor();
  354. }
  355. /**
  356. * Returns the <i>approximate</i> number of database pages owned by this
  357. * table and all related indexes (this number does <i>not</i> take into
  358. * account pages used for large OLE/MEMO fields).
  359. * <p>
  360. * To calculate the approximate number of bytes owned by a table:
  361. * <code>
  362. * int approxTableBytes = (table.getApproximateOwnedPageCount() *
  363. * table.getFormat().PAGE_SIZE);
  364. * </code>
  365. * @usage _intermediate_method_
  366. */
  367. public int getApproximateOwnedPageCount() {
  368. // add a page for the table def (although that might actually be more than
  369. // one page)
  370. int count = _ownedPages.getPageCount() + 1;
  371. for(ColumnImpl col : _columns) {
  372. count += col.getOwnedPageCount();
  373. }
  374. // note, we count owned pages from _physical_ indexes, not logical indexes
  375. // (otherwise we could double count pages)
  376. for(IndexData indexData : _indexDatas) {
  377. count += indexData.getOwnedPageCount();
  378. }
  379. return count;
  380. }
  381. protected TempPageHolder getLongValueBuffer() {
  382. return _longValueBufferH;
  383. }
  384. @Override
  385. public List<ColumnImpl> getColumns() {
  386. return Collections.unmodifiableList(_columns);
  387. }
  388. @Override
  389. public ColumnImpl getColumn(String name) {
  390. for(ColumnImpl column : _columns) {
  391. if(column.getName().equalsIgnoreCase(name)) {
  392. return column;
  393. }
  394. }
  395. throw new IllegalArgumentException(withErrorContext(
  396. "Column with name " + name + " does not exist in this table"));
  397. }
  398. public boolean hasColumn(String name) {
  399. for(ColumnImpl column : _columns) {
  400. if(column.getName().equalsIgnoreCase(name)) {
  401. return true;
  402. }
  403. }
  404. return false;
  405. }
  406. @Override
  407. public PropertyMap getProperties() throws IOException {
  408. if(_props == null) {
  409. _props = getPropertyMaps().getDefault();
  410. }
  411. return _props;
  412. }
  413. @Override
  414. public LocalDateTime getCreatedDate() throws IOException {
  415. return getDatabase().getCreateDateForObject(_tableDefPageNumber);
  416. }
  417. @Override
  418. public LocalDateTime getUpdatedDate() throws IOException {
  419. return getDatabase().getUpdateDateForObject(_tableDefPageNumber);
  420. }
  421. /**
  422. * @return all PropertyMaps for this table (and columns)
  423. * @usage _advanced_method_
  424. */
  425. public PropertyMaps getPropertyMaps() throws IOException {
  426. if(_propertyMaps == null) {
  427. _propertyMaps = getDatabase().getPropertiesForObject(
  428. _tableDefPageNumber, this);
  429. }
  430. return _propertyMaps;
  431. }
  432. @Override
  433. public void propertiesUpdated() throws IOException {
  434. // propagate update to columns
  435. for(ColumnImpl col : _columns) {
  436. col.propertiesUpdated();
  437. }
  438. reloadRowValidator();
  439. // calculated columns will need to be re-sorted (their expressions may
  440. // have changed when their properties were updated)
  441. _calcColEval.reSort();
  442. }
  443. @Override
  444. public List<IndexImpl> getIndexes() {
  445. return Collections.unmodifiableList(_indexes);
  446. }
  447. @Override
  448. public IndexImpl getIndex(String name) {
  449. for(IndexImpl index : _indexes) {
  450. if(index.getName().equalsIgnoreCase(name)) {
  451. return index;
  452. }
  453. }
  454. throw new IllegalArgumentException(withErrorContext(
  455. "Index with name " + name + " does not exist on this table"));
  456. }
  457. @Override
  458. public IndexImpl getPrimaryKeyIndex() {
  459. for(IndexImpl index : _indexes) {
  460. if(index.isPrimaryKey()) {
  461. return index;
  462. }
  463. }
  464. throw new IllegalArgumentException(withErrorContext(
  465. "No primary key index found"));
  466. }
  467. @Override
  468. public IndexImpl getForeignKeyIndex(Table otherTable) {
  469. for(IndexImpl index : _indexes) {
  470. if(index.isForeignKey() && (index.getReference() != null) &&
  471. (index.getReference().getOtherTablePageNumber() ==
  472. ((TableImpl)otherTable).getTableDefPageNumber())) {
  473. return index;
  474. }
  475. }
  476. throw new IllegalArgumentException(withErrorContext(
  477. "No foreign key reference to " +
  478. otherTable.getName() + " found"));
  479. }
  480. /**
  481. * @return All of the IndexData on this table (unmodifiable List)
  482. * @usage _advanced_method_
  483. */
  484. public List<IndexData> getIndexDatas() {
  485. return Collections.unmodifiableList(_indexDatas);
  486. }
  487. /**
  488. * Only called by unit tests
  489. * @usage _advanced_method_
  490. */
  491. public int getLogicalIndexCount() {
  492. return _logicalIndexCount;
  493. }
  494. int getIndexCount() {
  495. return _indexCount;
  496. }
  497. public IndexImpl findIndexForColumns(Collection<String> searchColumns,
  498. IndexFeature feature) {
  499. IndexImpl partialIndex = null;
  500. for(IndexImpl index : _indexes) {
  501. Collection<? extends Index.Column> indexColumns = index.getColumns();
  502. if(indexColumns.size() < searchColumns.size()) {
  503. continue;
  504. }
  505. boolean exactMatch = (indexColumns.size() == searchColumns.size());
  506. Iterator<String> sIter = searchColumns.iterator();
  507. Iterator<? extends Index.Column> iIter = indexColumns.iterator();
  508. boolean searchMatches = true;
  509. while(sIter.hasNext()) {
  510. String sColName = sIter.next();
  511. String iColName = iIter.next().getName();
  512. if((sColName != iColName) &&
  513. ((sColName == null) || !sColName.equalsIgnoreCase(iColName))) {
  514. searchMatches = false;
  515. break;
  516. }
  517. }
  518. if(searchMatches) {
  519. if(exactMatch && ((feature != IndexFeature.EXACT_UNIQUE_ONLY) ||
  520. index.isUnique())) {
  521. return index;
  522. }
  523. if(!exactMatch && (feature == IndexFeature.ANY_MATCH) &&
  524. ((partialIndex == null) ||
  525. (indexColumns.size() < partialIndex.getColumnCount()))) {
  526. // this is a better partial index match
  527. partialIndex = index;
  528. }
  529. }
  530. }
  531. return partialIndex;
  532. }
  533. List<ColumnImpl> getAutoNumberColumns() {
  534. return _autoNumColumns;
  535. }
  536. @Override
  537. public CursorImpl getDefaultCursor() {
  538. if(_defaultCursor == null) {
  539. _defaultCursor = CursorImpl.createCursor(this);
  540. }
  541. return _defaultCursor;
  542. }
  543. @Override
  544. public CursorBuilder newCursor() {
  545. return new CursorBuilder(this);
  546. }
  547. @Override
  548. public void reset() {
  549. getDefaultCursor().reset();
  550. }
  551. @Override
  552. public Row deleteRow(Row row) throws IOException {
  553. deleteRow(row.getId());
  554. return row;
  555. }
  556. /**
  557. * Delete the row with the given id. Provided RowId must have previously
  558. * been returned from this Table.
  559. * @return the given rowId
  560. * @throws IllegalStateException if the given row is not valid
  561. * @usage _intermediate_method_
  562. */
  563. public RowId deleteRow(RowId rowId) throws IOException {
  564. deleteRow(getDefaultCursor().getRowState(), (RowIdImpl)rowId);
  565. return rowId;
  566. }
  567. /**
  568. * Delete the row for the given rowId.
  569. * @usage _advanced_method_
  570. */
  571. public void deleteRow(RowState rowState, RowIdImpl rowId)
  572. throws IOException
  573. {
  574. requireValidRowId(rowId);
  575. getPageChannel().startWrite();
  576. try {
  577. // ensure that the relevant row state is up-to-date
  578. ByteBuffer rowBuffer = positionAtRowHeader(rowState, rowId);
  579. if(rowState.isDeleted()) {
  580. // don't care about duplicate deletion
  581. return;
  582. }
  583. requireNonDeletedRow(rowState, rowId);
  584. // delete flag always gets set in the "header" row (even if data is on
  585. // overflow row)
  586. int pageNumber = rowState.getHeaderRowId().getPageNumber();
  587. int rowNumber = rowState.getHeaderRowId().getRowNumber();
  588. // attempt to fill in index column values
  589. Object[] rowValues = null;
  590. if(!_indexDatas.isEmpty()) {
  591. // move to row data to get index values
  592. rowBuffer = positionAtRowData(rowState, rowId);
  593. for(ColumnImpl idxCol : _indexColumns) {
  594. getRowColumn(getFormat(), rowBuffer, idxCol, rowState, null);
  595. }
  596. // use any read rowValues to help update the indexes
  597. rowValues = rowState.getRowCacheValues();
  598. // check foreign keys before proceeding w/ deletion
  599. _fkEnforcer.deleteRow(rowValues);
  600. // move back to the header
  601. rowBuffer = positionAtRowHeader(rowState, rowId);
  602. }
  603. // finally, pull the trigger
  604. int rowIndex = getRowStartOffset(rowNumber, getFormat());
  605. rowBuffer.putShort(rowIndex, (short)(rowBuffer.getShort(rowIndex)
  606. | DELETED_ROW_MASK | OVERFLOW_ROW_MASK));
  607. writeDataPage(rowBuffer, pageNumber);
  608. // update the indexes
  609. for(IndexData indexData : _indexDatas) {
  610. indexData.deleteRow(rowValues, rowId);
  611. }
  612. // make sure table def gets updated
  613. updateTableDefinition(-1);
  614. } finally {
  615. getPageChannel().finishWrite();
  616. }
  617. }
  618. @Override
  619. public Row getNextRow() throws IOException {
  620. return getDefaultCursor().getNextRow();
  621. }
  622. /**
  623. * Reads a single column from the given row.
  624. * @usage _advanced_method_
  625. */
  626. public Object getRowValue(RowState rowState, RowIdImpl rowId,
  627. ColumnImpl column)
  628. throws IOException
  629. {
  630. if(this != column.getTable()) {
  631. throw new IllegalArgumentException(withErrorContext(
  632. "Given column " + column + " is not from this table"));
  633. }
  634. requireValidRowId(rowId);
  635. // position at correct row
  636. ByteBuffer rowBuffer = positionAtRowData(rowState, rowId);
  637. requireNonDeletedRow(rowState, rowId);
  638. return getRowColumn(getFormat(), rowBuffer, column, rowState, null);
  639. }
  640. /**
  641. * Reads some columns from the given row.
  642. * @param columnNames Only column names in this collection will be returned
  643. * @usage _advanced_method_
  644. */
  645. public RowImpl getRow(
  646. RowState rowState, RowIdImpl rowId, Collection<String> columnNames)
  647. throws IOException
  648. {
  649. requireValidRowId(rowId);
  650. // position at correct row
  651. ByteBuffer rowBuffer = positionAtRowData(rowState, rowId);
  652. requireNonDeletedRow(rowState, rowId);
  653. return getRow(getFormat(), rowState, rowBuffer, _columns, columnNames);
  654. }
  655. /**
  656. * Reads the row data from the given row buffer. Leaves limit unchanged.
  657. * Saves parsed row values to the given rowState.
  658. */
  659. private static RowImpl getRow(
  660. JetFormat format,
  661. RowState rowState,
  662. ByteBuffer rowBuffer,
  663. Collection<ColumnImpl> columns,
  664. Collection<String> columnNames)
  665. throws IOException
  666. {
  667. RowImpl rtn = new RowImpl(rowState.getHeaderRowId(), columns.size());
  668. for(ColumnImpl column : columns) {
  669. if((columnNames == null) || (columnNames.contains(column.getName()))) {
  670. // Add the value to the row data
  671. column.setRowValue(
  672. rtn, getRowColumn(format, rowBuffer, column, rowState, null));
  673. }
  674. }
  675. return rtn;
  676. }
  677. /**
  678. * Reads the column data from the given row buffer. Leaves limit unchanged.
  679. * Caches the returned value in the rowState.
  680. */
  681. private static Object getRowColumn(JetFormat format,
  682. ByteBuffer rowBuffer,
  683. ColumnImpl column,
  684. RowState rowState,
  685. Map<ColumnImpl,byte[]> rawVarValues)
  686. throws IOException
  687. {
  688. byte[] columnData = null;
  689. try {
  690. NullMask nullMask = rowState.getNullMask(rowBuffer);
  691. boolean isNull = nullMask.isNull(column);
  692. if(column.storeInNullMask()) {
  693. // Boolean values are stored in the null mask. see note about
  694. // caching below
  695. return rowState.setRowCacheValue(column.getColumnIndex(),
  696. column.readFromNullMask(isNull));
  697. } else if(isNull) {
  698. // well, that's easy! (no need to update cache w/ null)
  699. return null;
  700. }
  701. Object cachedValue = rowState.getRowCacheValue(column.getColumnIndex());
  702. if(cachedValue != null) {
  703. // we already have it, use it
  704. return cachedValue;
  705. }
  706. // reset position to row start
  707. rowBuffer.reset();
  708. // locate the column data bytes
  709. int rowStart = rowBuffer.position();
  710. int colDataPos = 0;
  711. int colDataLen = 0;
  712. if(!column.isVariableLength()) {
  713. // read fixed length value (non-boolean at this point)
  714. int dataStart = rowStart + format.OFFSET_COLUMN_FIXED_DATA_ROW_OFFSET;
  715. colDataPos = dataStart + column.getFixedDataOffset();
  716. colDataLen = column.getType().getFixedSize(column.getLength());
  717. } else {
  718. int varDataStart;
  719. int varDataEnd;
  720. if(format.SIZE_ROW_VAR_COL_OFFSET == 2) {
  721. // read simple var length value
  722. int varColumnOffsetPos =
  723. (rowBuffer.limit() - nullMask.byteSize() - 4) -
  724. (column.getVarLenTableIndex() * 2);
  725. varDataStart = rowBuffer.getShort(varColumnOffsetPos);
  726. varDataEnd = rowBuffer.getShort(varColumnOffsetPos - 2);
  727. } else {
  728. // read jump-table based var length values
  729. short[] varColumnOffsets = readJumpTableVarColOffsets(
  730. rowState, rowBuffer, rowStart, nullMask);
  731. varDataStart = varColumnOffsets[column.getVarLenTableIndex()];
  732. varDataEnd = varColumnOffsets[column.getVarLenTableIndex() + 1];
  733. }
  734. colDataPos = rowStart + varDataStart;
  735. colDataLen = varDataEnd - varDataStart;
  736. }
  737. // grab the column data
  738. rowBuffer.position(colDataPos);
  739. columnData = ByteUtil.getBytes(rowBuffer, colDataLen);
  740. if((rawVarValues != null) && column.isVariableLength()) {
  741. // caller wants raw value as well
  742. rawVarValues.put(column, columnData);
  743. }
  744. // parse the column data. we cache the row values in order to be able
  745. // to update the index on row deletion. note, most of the returned
  746. // values are immutable, except for binary data (returned as byte[]),
  747. // but binary data shouldn't be indexed anyway.
  748. return rowState.setRowCacheValue(column.getColumnIndex(),
  749. column.read(columnData));
  750. } catch(Exception e) {
  751. // cache "raw" row value. see note about caching above
  752. rowState.setRowCacheValue(column.getColumnIndex(),
  753. ColumnImpl.rawDataWrapper(columnData));
  754. return rowState.handleRowError(column, columnData, e);
  755. }
  756. }
  757. private static short[] readJumpTableVarColOffsets(
  758. RowState rowState, ByteBuffer rowBuffer, int rowStart,
  759. NullMask nullMask)
  760. {
  761. short[] varColOffsets = rowState.getVarColOffsets();
  762. if(varColOffsets != null) {
  763. return varColOffsets;
  764. }
  765. // calculate offsets using jump-table info
  766. int nullMaskSize = nullMask.byteSize();
  767. int rowEnd = rowStart + rowBuffer.remaining() - 1;
  768. int numVarCols = ByteUtil.getUnsignedByte(rowBuffer,
  769. rowEnd - nullMaskSize);
  770. varColOffsets = new short[numVarCols + 1];
  771. int rowLen = rowEnd - rowStart + 1;
  772. int numJumps = (rowLen - 1) / MAX_BYTE;
  773. int colOffset = rowEnd - nullMaskSize - numJumps - 1;
  774. // If last jump is a dummy value, ignore it
  775. if(((colOffset - rowStart - numVarCols) / MAX_BYTE) < numJumps) {
  776. numJumps--;
  777. }
  778. int jumpsUsed = 0;
  779. for(int i = 0; i < numVarCols + 1; i++) {
  780. while((jumpsUsed < numJumps) &&
  781. (i == ByteUtil.getUnsignedByte(
  782. rowBuffer, rowEnd - nullMaskSize-jumpsUsed - 1))) {
  783. jumpsUsed++;
  784. }
  785. varColOffsets[i] = (short)
  786. (ByteUtil.getUnsignedByte(rowBuffer, colOffset - i)
  787. + (jumpsUsed * MAX_BYTE));
  788. }
  789. rowState.setVarColOffsets(varColOffsets);
  790. return varColOffsets;
  791. }
  792. /**
  793. * Reads the null mask from the given row buffer. Leaves limit unchanged.
  794. */
  795. private NullMask getRowNullMask(ByteBuffer rowBuffer)
  796. throws IOException
  797. {
  798. // reset position to row start
  799. rowBuffer.reset();
  800. // Number of columns in this row
  801. int columnCount = ByteUtil.getUnsignedVarInt(
  802. rowBuffer, getFormat().SIZE_ROW_COLUMN_COUNT);
  803. // read null mask
  804. NullMask nullMask = new NullMask(columnCount);
  805. rowBuffer.position(rowBuffer.limit() - nullMask.byteSize()); //Null mask at end
  806. nullMask.read(rowBuffer);
  807. return nullMask;
  808. }
  809. /**
  810. * Sets a new buffer to the correct row header page using the given rowState
  811. * according to the given rowId. Deleted state is
  812. * determined, but overflow row pointers are not followed.
  813. *
  814. * @return a ByteBuffer of the relevant page, or null if row was invalid
  815. * @usage _advanced_method_
  816. */
  817. public static ByteBuffer positionAtRowHeader(RowState rowState,
  818. RowIdImpl rowId)
  819. throws IOException
  820. {
  821. ByteBuffer rowBuffer = rowState.setHeaderRow(rowId);
  822. if(rowState.isAtHeaderRow()) {
  823. // this task has already been accomplished
  824. return rowBuffer;
  825. }
  826. if(!rowState.isValid()) {
  827. // this was an invalid page/row
  828. rowState.setStatus(RowStateStatus.AT_HEADER);
  829. return null;
  830. }
  831. // note, we don't use findRowStart here cause we need the unmasked value
  832. short rowStart = rowBuffer.getShort(
  833. getRowStartOffset(rowId.getRowNumber(),
  834. rowState.getTable().getFormat()));
  835. // check the deleted, overflow flags for the row (the "real" flags are
  836. // always set on the header row)
  837. RowStatus rowStatus = RowStatus.NORMAL;
  838. if(isDeletedRow(rowStart)) {
  839. rowStatus = RowStatus.DELETED;
  840. } else if(isOverflowRow(rowStart)) {
  841. rowStatus = RowStatus.OVERFLOW;
  842. }
  843. rowState.setRowStatus(rowStatus);
  844. rowState.setStatus(RowStateStatus.AT_HEADER);
  845. return rowBuffer;
  846. }
  847. /**
  848. * Sets the position and limit in a new buffer using the given rowState
  849. * according to the given row number and row end, following overflow row
  850. * pointers as necessary.
  851. *
  852. * @return a ByteBuffer narrowed to the actual row data, or null if row was
  853. * invalid or deleted
  854. * @usage _advanced_method_
  855. */
  856. public static ByteBuffer positionAtRowData(RowState rowState,
  857. RowIdImpl rowId)
  858. throws IOException
  859. {
  860. positionAtRowHeader(rowState, rowId);
  861. if(!rowState.isValid() || rowState.isDeleted()) {
  862. // row is invalid or deleted
  863. rowState.setStatus(RowStateStatus.AT_FINAL);
  864. return null;
  865. }
  866. ByteBuffer rowBuffer = rowState.getFinalPage();
  867. int rowNum = rowState.getFinalRowId().getRowNumber();
  868. JetFormat format = rowState.getTable().getFormat();
  869. if(rowState.isAtFinalRow()) {
  870. // we've already found the final row data
  871. return PageChannel.narrowBuffer(
  872. rowBuffer,
  873. findRowStart(rowBuffer, rowNum, format),
  874. findRowEnd(rowBuffer, rowNum, format));
  875. }
  876. while(true) {
  877. // note, we don't use findRowStart here cause we need the unmasked value
  878. short rowStart = rowBuffer.getShort(getRowStartOffset(rowNum, format));
  879. short rowEnd = findRowEnd(rowBuffer, rowNum, format);
  880. // note, at this point we know the row is not deleted, so ignore any
  881. // subsequent deleted flags (as overflow rows are always marked deleted
  882. // anyway)
  883. boolean overflowRow = isOverflowRow(rowStart);
  884. // now, strip flags from rowStart offset
  885. rowStart = (short)(rowStart & OFFSET_MASK);
  886. if (overflowRow) {
  887. if((rowEnd - rowStart) < 4) {
  888. throw new IOException(rowState.getTable().withErrorContext(
  889. "invalid overflow row info"));
  890. }
  891. // Overflow page. the "row" data in the current page points to
  892. // another page/row
  893. int overflowRowNum = ByteUtil.getUnsignedByte(rowBuffer, rowStart);
  894. int overflowPageNum = ByteUtil.get3ByteInt(rowBuffer, rowStart + 1);
  895. rowBuffer = rowState.setOverflowRow(
  896. new RowIdImpl(overflowPageNum, overflowRowNum));
  897. rowNum = overflowRowNum;
  898. } else {
  899. rowState.setStatus(RowStateStatus.AT_FINAL);
  900. return PageChannel.narrowBuffer(rowBuffer, rowStart, rowEnd);
  901. }
  902. }
  903. }
  904. @Override
  905. public Iterator<Row> iterator() {
  906. return getDefaultCursor().iterator();
  907. }
  908. /**
  909. * Writes a new table defined by the given TableCreator to the database.
  910. * @usage _advanced_method_
  911. */
  912. protected static void writeTableDefinition(TableCreator creator)
  913. throws IOException
  914. {
  915. // first, create the usage map page
  916. createUsageMapDefinitionBuffer(creator);
  917. // next, determine how big the table def will be (in case it will be more
  918. // than one page)
  919. JetFormat format = creator.getFormat();
  920. int idxDataLen = (creator.getIndexCount() *
  921. (format.SIZE_INDEX_DEFINITION +
  922. format.SIZE_INDEX_COLUMN_BLOCK)) +
  923. (creator.getLogicalIndexCount() * format.SIZE_INDEX_INFO_BLOCK);
  924. int colUmapLen = creator.getLongValueColumns().size() * 10;
  925. int totalTableDefSize = format.SIZE_TDEF_HEADER +
  926. (format.SIZE_COLUMN_DEF_BLOCK * creator.getColumns().size()) +
  927. idxDataLen + colUmapLen + format.SIZE_TDEF_TRAILER;
  928. // total up the amount of space used by the column and index names (2
  929. // bytes per char + 2 bytes for the length)
  930. for(ColumnBuilder col : creator.getColumns()) {
  931. totalTableDefSize += DBMutator.calculateNameLength(col.getName());
  932. }
  933. for(IndexBuilder idx : creator.getIndexes()) {
  934. totalTableDefSize += DBMutator.calculateNameLength(idx.getName());
  935. }
  936. // now, create the table definition
  937. ByteBuffer buffer = PageChannel.createBuffer(Math.max(totalTableDefSize,
  938. format.PAGE_SIZE));
  939. writeTableDefinitionHeader(creator, buffer, totalTableDefSize);
  940. if(creator.hasIndexes()) {
  941. // index row counts
  942. IndexData.writeRowCountDefinitions(creator, buffer);
  943. }
  944. // column definitions
  945. ColumnImpl.writeDefinitions(creator, buffer);
  946. if(creator.hasIndexes()) {
  947. // index and index data definitions
  948. IndexData.writeDefinitions(creator, buffer);
  949. IndexImpl.writeDefinitions(creator, buffer);
  950. }
  951. // column usage map references
  952. ColumnImpl.writeColUsageMapDefinitions(creator, buffer);
  953. //End of tabledef
  954. buffer.put((byte) 0xff);
  955. buffer.put((byte) 0xff);
  956. buffer.flip();
  957. // write table buffer to database
  958. writeTableDefinitionBuffer(buffer, creator.getTdefPageNumber(), creator,
  959. Collections.<Integer>emptyList());
  960. }
  961. private static void writeTableDefinitionBuffer(
  962. ByteBuffer buffer, int tdefPageNumber,
  963. TableMutator mutator, List<Integer> reservedPages)
  964. throws IOException
  965. {
  966. buffer.rewind();
  967. int totalTableDefSize = buffer.remaining();
  968. JetFormat format = mutator.getFormat();
  969. PageChannel pageChannel = mutator.getPageChannel();
  970. // write table buffer to database
  971. if(totalTableDefSize <= format.PAGE_SIZE) {
  972. // easy case, fits on one page
  973. // overwrite page free space
  974. buffer.putShort(format.OFFSET_FREE_SPACE,
  975. (short)(Math.max(
  976. format.PAGE_SIZE - totalTableDefSize - 8, 0)));
  977. // Write the tdef page to disk.
  978. buffer.clear();
  979. pageChannel.writePage(buffer, tdefPageNumber);
  980. } else {
  981. // need to split across multiple pages
  982. ByteBuffer partialTdef = pageChannel.createPageBuffer();
  983. buffer.rewind();
  984. int nextTdefPageNumber = PageChannel.INVALID_PAGE_NUMBER;
  985. while(buffer.hasRemaining()) {
  986. // reset for next write
  987. partialTdef.clear();
  988. if(nextTdefPageNumber == PageChannel.INVALID_PAGE_NUMBER) {
  989. // this is the first page. note, the first page already has the
  990. // page header, so no need to write it here
  991. nextTdefPageNumber = tdefPageNumber;
  992. } else {
  993. // write page header
  994. writeTablePageHeader(partialTdef);
  995. }
  996. // copy the next page of tdef bytes
  997. int curTdefPageNumber = nextTdefPageNumber;
  998. int writeLen = Math.min(partialTdef.remaining(), buffer.remaining());
  999. partialTdef.put(buffer.array(), buffer.position(), writeLen);
  1000. ByteUtil.forward(buffer, writeLen);
  1001. if(buffer.hasRemaining()) {
  1002. // need a next page
  1003. if(reservedPages.isEmpty()) {
  1004. nextTdefPageNumber = pageChannel.allocateNewPage();
  1005. } else {
  1006. nextTdefPageNumber = reservedPages.remove(0);
  1007. }
  1008. partialTdef.putInt(format.OFFSET_NEXT_TABLE_DEF_PAGE,
  1009. nextTdefPageNumber);
  1010. }
  1011. // update page free space
  1012. partialTdef.putShort(format.OFFSET_FREE_SPACE,
  1013. (short)(Math.max(
  1014. partialTdef.remaining() - 8, 0)));
  1015. // write partial page to disk
  1016. pageChannel.writePage(partialTdef, curTdefPageNumber);
  1017. }
  1018. }
  1019. }
  1020. /**
  1021. * Writes a column defined by the given TableUpdater to this table.
  1022. * @usage _advanced_method_
  1023. */
  1024. protected ColumnImpl mutateAddColumn(TableUpdater mutator) throws IOException
  1025. {
  1026. ColumnBuilder column = mutator.getColumn();
  1027. JetFormat format = mutator.getFormat();
  1028. boolean isVarCol = column.isVariableLength();
  1029. boolean isLongVal = column.getType().isLongValue();
  1030. ////
  1031. // calculate how much more space we need in the table def
  1032. if(isLongVal) {
  1033. mutator.addTdefLen(10);
  1034. }
  1035. mutator.addTdefLen(format.SIZE_COLUMN_DEF_BLOCK);
  1036. int nameByteLen = DBMutator.calculateNameLength(column.getName());
  1037. mutator.addTdefLen(nameByteLen);
  1038. ////
  1039. // load current table definition and add space for new info
  1040. ByteBuffer tableBuffer = loadCompleteTableDefinitionBufferForUpdate(
  1041. mutator);
  1042. ColumnImpl newCol = null;
  1043. int umapPos = -1;
  1044. boolean success = false;
  1045. try {
  1046. ////
  1047. // update various bits of the table def
  1048. ByteUtil.forward(tableBuffer, 29);
  1049. tableBuffer.putShort((short)(_maxColumnCount + 1));
  1050. short varColCount = (short)(_varColumns.size() + (isVarCol ? 1 : 0));
  1051. tableBuffer.putShort(varColCount);
  1052. tableBuffer.putShort((short)(_columns.size() + 1));
  1053. // move to end of column def blocks
  1054. tableBuffer.position(format.SIZE_TDEF_HEADER +
  1055. (_indexCount * format.SIZE_INDEX_DEFINITION) +
  1056. (_columns.size() * format.SIZE_COLUMN_DEF_BLOCK));
  1057. // figure out the data offsets for the new column
  1058. int fixedOffset = 0;
  1059. int varOffset = 0;
  1060. if(column.isVariableLength()) {
  1061. // find the variable offset
  1062. for(ColumnImpl col : _varColumns) {
  1063. if(col.getVarLenTableIndex() >= varOffset) {
  1064. varOffset = col.getVarLenTableIndex() + 1;
  1065. }
  1066. }
  1067. } else {
  1068. // find the fixed offset
  1069. for(ColumnImpl col : _columns) {
  1070. if(!col.isVariableLength() &&
  1071. (col.getFixedDataOffset() >= fixedOffset)) {
  1072. fixedOffset = col.getFixedDataOffset() +
  1073. col.getType().getFixedSize(col.getLength());
  1074. }
  1075. }
  1076. }
  1077. mutator.setColumnOffsets(fixedOffset, varOffset, varOffset);
  1078. // insert space for the column definition and write it
  1079. int colDefPos = tableBuffer.position();
  1080. ByteUtil.insertEmptyData(tableBuffer, format.SIZE_COLUMN_DEF_BLOCK);
  1081. ColumnImpl.writeDefinition(mutator, column, tableBuffer);
  1082. // skip existing column names and write new name
  1083. skipNames(tableBuffer, _columns.size());
  1084. ByteUtil.insertEmptyData(tableBuffer, nameByteLen);
  1085. writeName(tableBuffer, column.getName(), mutator.getCharset());
  1086. if(isLongVal) {
  1087. // allocate usage maps for the long value col
  1088. Map.Entry<Integer,Integer> umapInfo = addUsageMaps(2, null);
  1089. TableMutator.ColumnState colState = mutator.getColumnState(column);
  1090. colState.setUmapPageNumber(umapInfo.getKey());
  1091. byte rowNum = umapInfo.getValue().byteValue();
  1092. colState.setUmapOwnedRowNumber(rowNum);
  1093. colState.setUmapFreeRowNumber((byte)(rowNum + 1));
  1094. // skip past index defs
  1095. ByteUtil.forward(tableBuffer, (_indexCount *
  1096. format.SIZE_INDEX_COLUMN_BLOCK));
  1097. ByteUtil.forward(tableBuffer,
  1098. (_logicalIndexCount * format.SIZE_INDEX_INFO_BLOCK));
  1099. skipNames(tableBuffer, _logicalIndexCount);
  1100. // skip existing usage maps
  1101. while(tableBuffer.remaining() >= 2) {
  1102. if(tableBuffer.getShort() == IndexData.COLUMN_UNUSED) {
  1103. // found end of tdef, we want to insert before this
  1104. ByteUtil.forward(tableBuffer, -2);
  1105. break;
  1106. }
  1107. ByteUtil.forward(tableBuffer, 8);
  1108. // keep reading ...
  1109. }
  1110. // write new column usage map info
  1111. umapPos = tableBuffer.position();
  1112. ByteUtil.insertEmptyData(tableBuffer, 10);
  1113. ColumnImpl.writeColUsageMapDefinition(
  1114. mutator, column, tableBuffer);
  1115. }
  1116. // sanity check the updates
  1117. validateTableDefUpdate(mutator, tableBuffer);
  1118. // before writing the new table def, create the column
  1119. newCol = ColumnImpl.create(this, tableBuffer, colDefPos,
  1120. column.getName(), _columns.size());
  1121. newCol.setColumnIndex(_columns.size());
  1122. ////
  1123. // write updated table def back to the database
  1124. writeTableDefinitionBuffer(tableBuffer, _tableDefPageNumber, mutator,
  1125. mutator.getNextPages());
  1126. success = true;
  1127. } finally {
  1128. if(!success) {
  1129. // need to discard modified table buffer
  1130. _tableDefBufferH.invalidate();
  1131. }
  1132. }
  1133. ////
  1134. // now, update current TableImpl
  1135. _columns.add(newCol);
  1136. ++_maxColumnCount;
  1137. if(newCol.isVariableLength()) {
  1138. _varColumns.add(newCol);
  1139. ++_maxVarColumnCount;
  1140. }
  1141. if(newCol.isAutoNumber()) {
  1142. _autoNumColumns.add(newCol);
  1143. }
  1144. if(newCol.isCalculated()) {
  1145. _calcColEval.add(newCol);
  1146. }
  1147. if(umapPos >= 0) {
  1148. // read column usage map
  1149. tableBuffer.position(umapPos);
  1150. readColumnUsageMaps(tableBuffer);
  1151. }
  1152. newCol.postTableLoadInit();
  1153. if(!isSystem()) {
  1154. // after fully constructed, allow column validator to be configured (but
  1155. // only for user tables)
  1156. newCol.initColumnValidator();
  1157. }
  1158. // save any column properties
  1159. Map<String,PropertyMap.Property> colProps = column.getProperties();
  1160. if(colProps != null) {
  1161. newCol.getProperties().putAll(colProps.values());
  1162. getProperties().save();
  1163. }
  1164. completeTableMutation(tableBuffer);
  1165. return newCol;
  1166. }
  1167. /**
  1168. * Writes a index defined by the given TableUpdater to this table.
  1169. * @usage _advanced_method_
  1170. */
  1171. protected IndexData mutateAddIndexData(TableUpdater mutator) throws IOException
  1172. {
  1173. IndexBuilder index = mutator.getIndex();
  1174. JetFormat format = mutator.getFormat();
  1175. ////
  1176. // calculate how much more space we need in the table def
  1177. mutator.addTdefLen(format.SIZE_INDEX_DEFINITION +
  1178. format.SIZE_INDEX_COLUMN_BLOCK);
  1179. ////
  1180. // load current table definition and add space for new info
  1181. ByteBuffer tableBuffer = loadCompleteTableDefinitionBufferForUpdate(
  1182. mutator);
  1183. IndexData newIdxData = null;
  1184. boolean success = false;
  1185. try {
  1186. ////
  1187. // update various bits of the table def
  1188. ByteUtil.forward(tableBuffer, 39);
  1189. tableBuffer.putInt(_indexCount + 1);
  1190. // move to end of index data def blocks
  1191. tableBuffer.position(format.SIZE_TDEF_HEADER +
  1192. (_indexCount * format.SIZE_INDEX_DEFINITION));
  1193. // write index row count definition (empty initially)
  1194. ByteUtil.insertEmptyData(tableBuffer, format.SIZE_INDEX_DEFINITION);
  1195. IndexData.writeRowCountDefinitions(mutator, tableBuffer, 1);
  1196. // skip columns and column names
  1197. ByteUtil.forward(tableBuffer,
  1198. (_columns.size() * format.SIZE_COLUMN_DEF_BLOCK));
  1199. skipNames(tableBuffer, _columns.size());
  1200. // move to end of current index datas
  1201. ByteUtil.forward(tableBuffer, (_indexCount *
  1202. format.SIZE_INDEX_COLUMN_BLOCK));
  1203. // allocate usage maps and root page
  1204. TableMutator.IndexDataState idxDataState = mutator.getIndexDataState(index);
  1205. int rootPageNumber = getPageChannel().allocateNewPage();
  1206. Map.Entry<Integer,Integer> umapInfo = addUsageMaps(1, rootPageNumber);
  1207. idxDataState.setRootPageNumber(rootPageNumber);
  1208. idxDataState.setUmapPageNumber(umapInfo.getKey());
  1209. idxDataState.setUmapRowNumber(umapInfo.getValue().byteValue());
  1210. // write index data def
  1211. int idxDataDefPos = tableBuffer.position();
  1212. ByteUtil.insertEmptyData(tableBuffer, format.SIZE_INDEX_COLUMN_BLOCK);
  1213. IndexData.writeDefinition(mutator, tableBuffer, idxDataState, null);
  1214. // sanity check the updates
  1215. validateTableDefUpdate(mutator, tableBuffer);
  1216. // before writing the new table def, create the index data
  1217. tableBuffer.position(0);
  1218. newIdxData = IndexData.create(
  1219. this, tableBuffer, idxDataState.getIndexDataNumber(), format);
  1220. tableBuffer.position(idxDataDefPos);
  1221. newIdxData.read(tableBuffer, _columns);
  1222. ////
  1223. // write updated table def back to the database
  1224. writeTableDefinitionBuffer(tableBuffer, _tableDefPageNumber, mutator,
  1225. mutator.getNextPages());
  1226. success = true;
  1227. } finally {
  1228. if(!success) {
  1229. // need to discard modified table buffer
  1230. _tableDefBufferH.invalidate();
  1231. }
  1232. }
  1233. ////
  1234. // now, update current TableImpl
  1235. for(IndexData.ColumnDescriptor iCol : newIdxData.getColumns()) {
  1236. _indexColumns.add(iCol.getColumn());
  1237. }
  1238. ++_indexCount;
  1239. _indexDatas.add(newIdxData);
  1240. completeTableMutation(tableBuffer);
  1241. // don't forget to populate the new index
  1242. populateIndexData(newIdxData);
  1243. return newIdxData;
  1244. }
  1245. private void populateIndexData(IndexData idxData)
  1246. throws IOException
  1247. {
  1248. // grab the columns involved in this index
  1249. List<ColumnImpl> idxCols = new ArrayList<ColumnImpl>();
  1250. for(IndexData.ColumnDescriptor col : idxData.getColumns()) {
  1251. idxCols.add(col.getColumn());
  1252. }
  1253. // iterate through all the rows and add them to the index
  1254. Object[] rowVals = new Object[_columns.size()];
  1255. for(Row row : getDefaultCursor().newIterable().addColumns(idxCols)) {
  1256. for(Column col : idxCols) {
  1257. col.setRowValue(rowVals, col.getRowValue(row));
  1258. }
  1259. IndexData.commitAll(
  1260. idxData.prepareAddRow(rowVals, (RowIdImpl)row.getId(), null));
  1261. }
  1262. updateTableDefinition(0);
  1263. }
  1264. /**
  1265. * Writes a index defined by the given TableUpdater to this table.
  1266. * @usage _advanced_method_
  1267. */
  1268. protected IndexImpl mutateAddIndex(TableUpdater mutator) throws IOException
  1269. {
  1270. IndexBuilder index = mutator.getIndex();
  1271. JetFormat format = mutator.getFormat();
  1272. ////
  1273. // calculate how much more space we need in the table def
  1274. mutator.addTdefLen(format.SIZE_INDEX_INFO_BLOCK);
  1275. int nameByteLen = DBMutator.calculateNameLength(index.getName());
  1276. mutator.addTdefLen(nameByteLen);
  1277. ////
  1278. // load current table definition and add space for new info
  1279. ByteBuffer tableBuffer = loadCompleteTableDefinitionBufferForUpdate(
  1280. mutator);
  1281. IndexImpl newIdx = null;
  1282. boolean success = false;
  1283. try {
  1284. ////
  1285. // update various bits of the table def
  1286. ByteUtil.forward(tableBuffer, 35);
  1287. tableBuffer.putInt(_logicalIndexCount + 1);
  1288. // move to end of index data def blocks
  1289. tableBuffer.position(format.SIZE_TDEF_HEADER +
  1290. (_indexCount * format.SIZE_INDEX_DEFINITION));
  1291. // skip columns and column names
  1292. ByteUtil.forward(tableBuffer,
  1293. (_columns.size() * format.SIZE_COLUMN_DEF_BLOCK));
  1294. skipNames(tableBuffer, _columns.size());
  1295. // move to end of current index datas
  1296. ByteUtil.forward(tableBuffer, (_indexCount *
  1297. format.SIZE_INDEX_COLUMN_BLOCK));
  1298. // move to end of current indexes
  1299. ByteUtil.forward(tableBuffer, (_logicalIndexCount *
  1300. format.SIZE_INDEX_INFO_BLOCK));
  1301. int idxDefPos = tableBuffer.position();
  1302. ByteUtil.insertEmptyData(tableBuffer, format.SIZE_INDEX_INFO_BLOCK);
  1303. IndexImpl.writeDefinition(mutator, index, tableBuffer);
  1304. // skip existing index names and write new name
  1305. skipNames(tableBuffer, _logicalIndexCount);
  1306. ByteUtil.insertEmptyData(tableBuffer, nameByteLen);
  1307. writeName(tableBuffer, index.getName(), mutator.getCharset());
  1308. // sanity check the updates
  1309. validateTableDefUpdate(mutator, tableBuffer);
  1310. // before writing the new table def, create the index
  1311. tableBuffer.position(idxDefPos);
  1312. newIdx = new IndexImpl(tableBuffer, _indexDatas, format);
  1313. newIdx.setName(index.getName());
  1314. ////
  1315. // write updated table def back to the database
  1316. writeTableDefinitionBuffer(tableBuffer, _tableDefPageNumber, mutator,
  1317. mutator.getNextPages());
  1318. success = true;
  1319. } finally {
  1320. if(!success) {
  1321. // need to discard modified table buffer
  1322. _tableDefBufferH.invalidate();
  1323. }
  1324. }
  1325. ////
  1326. // now, update current TableImpl
  1327. ++_logicalIndexCount;
  1328. _indexes.add(newIdx);
  1329. completeTableMutation(tableBuffer);
  1330. return newIdx;
  1331. }
  1332. private void validateTableDefUpdate(TableUpdater mutator, ByteBuffer tableBuffer)
  1333. throws IOException
  1334. {
  1335. if(!mutator.validateUpdatedTdef(tableBuffer)) {
  1336. throw new IllegalStateException(
  1337. withErrorContext("Failed updating table definition (unexpected length)"));
  1338. }
  1339. }
  1340. private void completeTableMutation(ByteBuffer tableBuffer) throws IOException
  1341. {
  1342. // lastly, may need to clear table def buffer
  1343. _tableDefBufferH.possiblyInvalidate(_tableDefPageNumber, tableBuffer);
  1344. // update any foreign key enforcing
  1345. _fkEnforcer.reset();
  1346. // update modification count so any active RowStates can keep themselves
  1347. // up-to-date
  1348. ++_modCount;
  1349. }
  1350. /**
  1351. * Skips the given number of names in the table buffer.
  1352. */
  1353. private static void skipNames(ByteBuffer tableBuffer, int count) {
  1354. for(int i = 0; i < count; ++i) {
  1355. ByteUtil.forward(tableBuffer, tableBuffer.getShort());
  1356. }
  1357. }
  1358. private ByteBuffer loadCompleteTableDefinitionBufferForUpdate(
  1359. TableUpdater mutator)
  1360. throws IOException
  1361. {
  1362. // load complete table definition
  1363. ByteBuffer tableBuffer = _tableDefBufferH.setPage(getPageChannel(),
  1364. _tableDefPageNumber);
  1365. tableBuffer = loadCompleteTableDefinitionBuffer(
  1366. tableBuffer, mutator.getNextPages());
  1367. // make sure the table buffer has enough room for the new info
  1368. int addedLen = mutator.getAddedTdefLen();
  1369. int origTdefLen = tableBuffer.getInt(8);
  1370. mutator.setOrigTdefLen(origTdefLen);
  1371. int newTdefLen = origTdefLen + addedLen;
  1372. while(newTdefLen > tableBuffer.capacity()) {
  1373. tableBuffer = expandTableBuffer(tableBuffer);
  1374. tableBuffer.flip();
  1375. }
  1376. tableBuffer.limit(origTdefLen);
  1377. // set new tdef length
  1378. tableBuffer.position(8);
  1379. tableBuffer.putInt(newTdefLen);
  1380. return tableBuffer;
  1381. }
  1382. /**
  1383. * Adds some usage maps for use with this table. This method is expected to
  1384. * be called with a small-ish number of requested usage maps.
  1385. */
  1386. private Map.Entry<Integer,Integer> addUsageMaps(
  1387. int numMaps, Integer firstUsedPage)
  1388. throws IOException
  1389. {
  1390. JetFormat format = getFormat();
  1391. PageChannel pageChannel = getPageChannel();
  1392. int umapRowLength = format.OFFSET_USAGE_MAP_START +
  1393. format.USAGE_MAP_TABLE_BYTE_LENGTH;
  1394. int totalUmapSpaceUsage = getRowSpaceUsage(umapRowLength, format) * numMaps;
  1395. int umapPageNumber = PageChannel.INVALID_PAGE_NUMBER;
  1396. int firstRowNum = -1;
  1397. int freeSpace = 0;
  1398. // search currently known usage map buffers to find one with enough free
  1399. // space (the numMaps should always be small enough to put them all on one
  1400. // page). pages will free space will probaby be newer pages (higher
  1401. // numbers), so we sort in reverse order.
  1402. Set<Integer> knownPages = new TreeSet<Integer>(Collections.reverseOrder());
  1403. collectUsageMapPages(knownPages);
  1404. ByteBuffer umapBuf = pageChannel.createPageBuffer();
  1405. for(Integer pageNum : knownPages) {
  1406. pageChannel.readPage(umapBuf, pageNum);
  1407. freeSpace = umapBuf.getShort(format.OFFSET_FREE_SPACE);
  1408. if(freeSpace >= totalUmapSpaceUsage) {
  1409. // found a page!
  1410. umapPageNumber = pageNum;
  1411. firstRowNum = getRowsOnDataPage(umapBuf, format);
  1412. break;
  1413. }
  1414. }
  1415. if(umapPageNumber == PageChannel.INVALID_PAGE_NUMBER) {
  1416. // didn't find any existing pages, need to create a new one
  1417. umapPageNumber = pageChannel.allocateNewPage();
  1418. freeSpace = format.DATA_PAGE_INITIAL_FREE_SPACE;
  1419. firstRowNum = 0;
  1420. umapBuf = createUsageMapDefPage(pageChannel, freeSpace);
  1421. }
  1422. // write the actual usage map defs
  1423. int rowStart = findRowEnd(umapBuf, firstRowNum, format) - umapRowLength;
  1424. int umapRowNum = firstRowNum;
  1425. for(int i = 0; i < numMaps; ++i) {
  1426. umapBuf.putShort(getRowStartOffset(umapRowNum, format), (short)rowStart);
  1427. umapBuf.put(rowStart, UsageMap.MAP_TYPE_INLINE);
  1428. int dataOffset = rowStart + 1;
  1429. if(firstUsedPage != null) {
  1430. // fill in the first used page of the usage map
  1431. umapBuf.putInt(dataOffset, firstUsedPage);
  1432. dataOffset += 4;
  1433. umapBuf.put(dataOffset, (byte)1);
  1434. dataOffset++;
  1435. }
  1436. // zero remaining row data
  1437. ByteUtil.clearRange(umapBuf, dataOffset, (rowStart + umapRowLength));
  1438. rowStart -= umapRowLength;
  1439. ++umapRowNum;
  1440. }
  1441. // finish the page
  1442. freeSpace -= totalUmapSpaceUsage;
  1443. umapBuf.putShort(format.OFFSET_FREE_SPACE, (short)freeSpace);
  1444. umapBuf.putShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE,
  1445. (short)umapRowNum);
  1446. pageChannel.writePage(umapBuf, umapPageNumber);
  1447. return new AbstractMap.SimpleImmutableEntry<Integer,Integer>(
  1448. umapPageNumber, firstRowNum);
  1449. }
  1450. void collectUsageMapPages(Collection<Integer> pages) {
  1451. pages.add(_ownedPages.getTablePageNumber());
  1452. pages.add(_freeSpacePages.getTablePageNumber());
  1453. for(IndexData idx : _indexDatas) {
  1454. idx.collectUsageMapPages(pages);
  1455. }
  1456. for(ColumnImpl col : _columns) {
  1457. col.collectUsageMapPages(pages);
  1458. }
  1459. }
  1460. /**
  1461. * @param buffer Buffer to write to
  1462. */
  1463. private static void writeTableDefinitionHeader(
  1464. TableCreator creator, ByteBuffer buffer, int totalTableDefSize)
  1465. throws IOException
  1466. {
  1467. List<ColumnBuilder> columns = creator.getColumns();
  1468. //Start writing the tdef
  1469. writeTablePageHeader(buffer);
  1470. buffer.putInt(totalTableDefSize); //Length of table def
  1471. buffer.putInt(MAGIC_TABLE_NUMBER); // seemingly constant magic value
  1472. buffer.putInt(0); //Number of rows
  1473. buffer.putInt(0); //Last Autonumber
  1474. buffer.put((byte) 1); // this makes autonumbering work in access
  1475. for (int i = 0; i < 15; i++) { //Unknown
  1476. buffer.put((byte) 0);
  1477. }
  1478. buffer.put(TYPE_USER); //Table type
  1479. buffer.putShort((short) columns.size()); //Max columns a row will have
  1480. buffer.putShort(ColumnImpl.countVariableLength(columns)); //Number of variable columns in table
  1481. buffer.putShort((short) columns.size()); //Number of columns in table
  1482. buffer.putInt(creator.getLogicalIndexCount()); //Number of logical indexes in table
  1483. buffer.putInt(creator.getIndexCount()); //Number of indexes in table
  1484. buffer.put((byte) 0); //Usage map row number
  1485. ByteUtil.put3ByteInt(buffer, creator.getUmapPageNumber()); //Usage map page number
  1486. buffer.put((byte) 1); //Free map row number
  1487. ByteUtil.put3ByteInt(buffer, creator.getUmapPageNumber()); //Free map page number
  1488. }
  1489. /**
  1490. * Writes the page header for a table definition page
  1491. * @param buffer Buffer to write to
  1492. */
  1493. private static void writeTablePageHeader(ByteBuffer buffer)
  1494. {
  1495. buffer.put(PageTypes.TABLE_DEF); //Page type
  1496. buffer.put((byte) 0x01); //Unknown
  1497. buffer.put((byte) 0); //Unknown
  1498. buffer.put((byte) 0); //Unknown
  1499. buffer.putInt(0); //Next TDEF page pointer
  1500. }
  1501. /**
  1502. * Writes the given name into the given buffer in the format as expected by
  1503. * {@link #readName}.
  1504. */
  1505. static void writeName(ByteBuffer buffer, String name, Charset charset)
  1506. {
  1507. ByteBuffer encName = ColumnImpl.encodeUncompressedText(name, charset);
  1508. buffer.putShort((short) encName.remaining());
  1509. buffer.put(encName);
  1510. }
  1511. /**
  1512. * Create the usage map definition page buffer. The "used pages" map is in
  1513. * row 0, the "pages with free space" map is in row 1. Index usage maps are
  1514. * in subsequent rows.
  1515. */
  1516. private static void createUsageMapDefinitionBuffer(TableCreator creator)
  1517. throws IOException
  1518. {
  1519. List<ColumnBuilder> lvalCols = creator.getLongValueColumns();
  1520. // 2 table usage maps plus 1 for each index and 2 for each lval col
  1521. int indexUmapEnd = 2 + creator.getIndexCount();
  1522. int umapNum = indexUmapEnd + (lvalCols.size() * 2);
  1523. JetFormat format = creator.getFormat();
  1524. int umapRowLength = format.OFFSET_USAGE_MAP_START +
  1525. format.USAGE_MAP_TABLE_BYTE_LENGTH;
  1526. int umapSpaceUsage = getRowSpaceUsage(umapRowLength, format);
  1527. PageChannel pageChannel = creator.getPageChannel();
  1528. int umapPageNumber = PageChannel.INVALID_PAGE_NUMBER;
  1529. ByteBuffer umapBuf = null;
  1530. int freeSpace = 0;
  1531. int rowStart = 0;
  1532. int umapRowNum = 0;
  1533. for(int i = 0; i < umapNum; ++i) {
  1534. if(umapBuf == null) {
  1535. // need new page for usage maps
  1536. if(umapPageNumber == PageChannel.INVALID_PAGE_NUMBER) {
  1537. // first umap page has already been reserved
  1538. umapPageNumber = creator.getUmapPageNumber();
  1539. } else {
  1540. // need another umap page
  1541. umapPageNumber = creator.reservePageNumber();
  1542. }
  1543. freeSpace = format.DATA_PAGE_INITIAL_FREE_SPACE;
  1544. umapBuf = createUsageMapDefPage(pageChannel, freeSpace);
  1545. rowStart = findRowEnd(umapBuf, 0, format) - umapRowLength;
  1546. umapRowNum = 0;
  1547. }
  1548. umapBuf.putShort(getRowStartOffset(umapRowNum, format), (short)rowStart);
  1549. if(i == 0) {
  1550. // table "owned pages" map definition
  1551. umapBuf.put(rowStart, UsageMap.MAP_TYPE_REFERENCE);
  1552. } else if(i == 1) {
  1553. // table "free space pages" map definition
  1554. umapBuf.put(rowStart, UsageMap.MAP_TYPE_INLINE);
  1555. } else if(i < indexUmapEnd) {
  1556. // index umap
  1557. int indexIdx = i - 2;
  1558. TableMutator.IndexDataState idxDataState =
  1559. creator.getIndexDataStates().get(indexIdx);
  1560. // allocate root page for the index
  1561. int rootPageNumber = pageChannel.allocateNewPage();
  1562. // stash info for later use
  1563. idxDataState.setRootPageNumber(rootPageNumber);
  1564. idxDataState.setUmapRowNumber((byte)umapRowNum);
  1565. idxDataState.setUmapPageNumber(umapPageNumber);
  1566. // index map definition, including initial root page
  1567. umapBuf.put(rowStart, UsageMap.MAP_TYPE_INLINE);
  1568. umapBuf.putInt(rowStart + 1, rootPageNumber);
  1569. umapBuf.put(rowStart + 5, (byte)1);
  1570. } else {
  1571. // long value column umaps
  1572. int lvalColIdx = i - indexUmapEnd;
  1573. int umapType = lvalColIdx % 2;
  1574. lvalColIdx /= 2;
  1575. ColumnBuilder lvalCol = lvalCols.get(lvalColIdx);
  1576. TableMutator.ColumnState colState =
  1577. creator.getColumnState(lvalCol);
  1578. umapBuf.put(rowStart, UsageMap.MAP_TYPE_INLINE);
  1579. if((umapType == 1) &&
  1580. (umapPageNumber != colState.getUmapPageNumber())) {
  1581. // we want to force both usage maps for a column to be on the same
  1582. // data page, so just discard the previous one we wrote
  1583. --i;
  1584. umapType = 0;
  1585. }
  1586. if(umapType == 0) {
  1587. // lval column "owned pages" usage map
  1588. colState.setUmapOwnedRowNumber((byte)umapRowNum);
  1589. colState.setUmapPageNumber(umapPageNumber);
  1590. } else {
  1591. // lval column "free space pages" usage map (always on same page)
  1592. colState.setUmapFreeRowNumber((byte)umapRowNum);
  1593. }
  1594. }
  1595. rowStart -= umapRowLength;
  1596. freeSpace -= umapSpaceUsage;
  1597. ++umapRowNum;
  1598. if((freeSpace <= umapSpaceUsage) || (i == (umapNum - 1))) {
  1599. // finish current page
  1600. umapBuf.putShort(format.OFFSET_FREE_SPACE, (short)freeSpace);
  1601. umapBuf.putShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE,
  1602. (short)umapRowNum);
  1603. pageChannel.writePage(umapBuf, umapPageNumber);
  1604. umapBuf = null;
  1605. }
  1606. }
  1607. }
  1608. private static ByteBuffer createUsageMapDefPage(
  1609. PageChannel pageChannel, int freeSpace)
  1610. {
  1611. ByteBuffer umapBuf = pageChannel.createPageBuffer();
  1612. umapBuf.put(PageTypes.DATA);
  1613. umapBuf.put((byte) 0x1); //Unknown
  1614. umapBuf.putShort((short)freeSpace); //Free space in page
  1615. umapBuf.putInt(0); //Table definition
  1616. umapBuf.putInt(0); //Unknown
  1617. umapBuf.putShort((short)0); //Number of records on this page
  1618. return umapBuf;
  1619. }
  1620. /**
  1621. * Returns a single ByteBuffer which contains the entire table definition
  1622. * (which may span multiple database pages).
  1623. */
  1624. private ByteBuffer loadCompleteTableDefinitionBuffer(
  1625. ByteBuffer tableBuffer, List<Integer> pages)
  1626. throws IOException
  1627. {
  1628. int nextPage = tableBuffer.getInt(getFormat().OFFSET_NEXT_TABLE_DEF_PAGE);
  1629. ByteBuffer nextPageBuffer = null;
  1630. while (nextPage != 0) {
  1631. if(pages != null) {
  1632. pages.add(nextPage);
  1633. }
  1634. if (nextPageBuffer == null) {
  1635. nextPageBuffer = getPageChannel().createPageBuffer();
  1636. }
  1637. getPageChannel().readPage(nextPageBuffer, nextPage);
  1638. nextPage = nextPageBuffer.getInt(getFormat().OFFSET_NEXT_TABLE_DEF_PAGE);
  1639. tableBuffer = expandTableBuffer(tableBuffer);
  1640. tableBuffer.put(nextPageBuffer.array(), 8, getFormat().PAGE_SIZE - 8);
  1641. tableBuffer.flip();
  1642. }
  1643. return tableBuffer;
  1644. }
  1645. private ByteBuffer expandTableBuffer(ByteBuffer tableBuffer) {
  1646. ByteBuffer newBuffer = PageChannel.createBuffer(
  1647. tableBuffer.capacity() + getFormat().PAGE_SIZE - 8);
  1648. newBuffer.put(tableBuffer);
  1649. return newBuffer;
  1650. }
  1651. private void readColumnDefinitions(ByteBuffer tableBuffer, short columnCount)
  1652. throws IOException
  1653. {
  1654. int colOffset = getFormat().OFFSET_INDEX_DEF_BLOCK +
  1655. _indexCount * getFormat().SIZE_INDEX_DEFINITION;
  1656. tableBuffer.position(colOffset +
  1657. (columnCount * getFormat().SIZE_COLUMN_HEADER));
  1658. List<String> colNames = new ArrayList<String>(columnCount);
  1659. for (int i = 0; i < columnCount; i++) {
  1660. colNames.add(readName(tableBuffer));
  1661. }
  1662. int dispIndex = 0;
  1663. for (int i = 0; i < columnCount; i++) {
  1664. ColumnImpl column = ColumnImpl.create(this, tableBuffer,
  1665. colOffset + (i * getFormat().SIZE_COLUMN_HEADER), colNames.get(i),
  1666. dispIndex++);
  1667. _columns.add(column);
  1668. if(column.isVariableLength()) {
  1669. // also shove it in the variable columns list, which is ordered
  1670. // differently from the _columns list
  1671. _varColumns.add(column);
  1672. }
  1673. }
  1674. Collections.sort(_columns);
  1675. initAutoNumberColumns();
  1676. initCalculatedColumns();
  1677. // setup the data index for the columns
  1678. int colIdx = 0;
  1679. for(ColumnImpl col : _columns) {
  1680. col.setColumnIndex(colIdx++);
  1681. }
  1682. // sort variable length columns based on their index into the variable
  1683. // length offset table, because we will write the columns in this order
  1684. Collections.sort(_varColumns, VAR_LEN_COLUMN_COMPARATOR);
  1685. }
  1686. private void readIndexDefinitions(ByteBuffer tableBuffer) throws IOException
  1687. {
  1688. // read index column information
  1689. for (int i = 0; i < _indexCount; i++) {
  1690. IndexData idxData = _indexDatas.get(i);
  1691. idxData.read(tableBuffer, _columns);
  1692. // keep track of all columns involved in indexes
  1693. for(IndexData.ColumnDescriptor iCol : idxData.getColumns()) {
  1694. _indexColumns.add(iCol.getColumn());
  1695. }
  1696. }
  1697. // read logical index info (may be more logical indexes than index datas)
  1698. for (int i = 0; i < _logicalIndexCount; i++) {
  1699. _indexes.add(new IndexImpl(tableBuffer, _indexDatas, getFormat()));
  1700. }
  1701. // read logical index names
  1702. for (int i = 0; i < _logicalIndexCount; i++) {
  1703. _indexes.get(i).setName(readName(tableBuffer));
  1704. }
  1705. Collections.sort(_indexes);
  1706. }
  1707. private boolean readColumnUsageMaps(ByteBuffer tableBuffer)
  1708. throws IOException
  1709. {
  1710. short umapColNum = tableBuffer.getShort();
  1711. if(umapColNum == IndexData.COLUMN_UNUSED) {
  1712. return false;
  1713. }
  1714. int pos = tableBuffer.position();
  1715. UsageMap colOwnedPages = null;
  1716. UsageMap colFreeSpacePages = null;
  1717. try {
  1718. colOwnedPages = UsageMap.read(getDatabase(), tableBuffer);
  1719. colFreeSpacePages = UsageMap.read(getDatabase(), tableBuffer);
  1720. } catch(IllegalStateException e) {
  1721. // ignore invalid usage map info
  1722. colOwnedPages = null;
  1723. colFreeSpacePages = null;
  1724. tableBuffer.position(pos + 8);
  1725. LOG.warn(withErrorContext("Invalid column " + umapColNum +
  1726. " usage map definition: " + e));
  1727. }
  1728. for(ColumnImpl col : _columns) {
  1729. if(col.getColumnNumber() == umapColNum) {
  1730. col.setUsageMaps(colOwnedPages, colFreeSpacePages);
  1731. break;
  1732. }
  1733. }
  1734. return true;
  1735. }
  1736. /**
  1737. * Writes the given page data to the given page number, clears any other
  1738. * relevant buffers.
  1739. */
  1740. private void writeDataPage(ByteBuffer pageBuffer, int pageNumber)
  1741. throws IOException
  1742. {
  1743. // write the page data
  1744. getPageChannel().writePage(pageBuffer, pageNumber);
  1745. // possibly invalidate the add row buffer if a different data buffer is
  1746. // being written (e.g. this happens during deleteRow)
  1747. _addRowBufferH.possiblyInvalidate(pageNumber, pageBuffer);
  1748. // update modification count so any active RowStates can keep themselves
  1749. // up-to-date
  1750. ++_modCount;
  1751. }
  1752. /**
  1753. * Returns a name read from the buffer at the current position. The
  1754. * expected name format is the name length followed by the name
  1755. * encoded using the {@link JetFormat#CHARSET}
  1756. */
  1757. private String readName(ByteBuffer buffer) {
  1758. int nameLength = readNameLength(buffer);
  1759. byte[] nameBytes = ByteUtil.getBytes(buffer, nameLength);
  1760. return ColumnImpl.decodeUncompressedText(nameBytes,
  1761. getDatabase().getCharset());
  1762. }
  1763. /**
  1764. * Returns a name length read from the buffer at the current position.
  1765. */
  1766. private int readNameLength(ByteBuffer buffer) {
  1767. return ByteUtil.getUnsignedVarInt(buffer, getFormat().SIZE_NAME_LENGTH);
  1768. }
  1769. @Override
  1770. public Object[] asRow(Map<String,?> rowMap) {
  1771. return asRow(rowMap, null, false);
  1772. }
  1773. /**
  1774. * Converts a map of columnName -&gt; columnValue to an array of row values
  1775. * appropriate for a call to {@link #addRow(Object...)}, where the generated
  1776. * RowId will be an extra value at the end of the array.
  1777. * @see ColumnImpl#RETURN_ROW_ID
  1778. * @usage _intermediate_method_
  1779. */
  1780. public Object[] asRowWithRowId(Map<String,?> rowMap) {
  1781. return asRow(rowMap, null, true);
  1782. }
  1783. @Override
  1784. public Object[] asUpdateRow(Map<String,?> rowMap) {
  1785. return asRow(rowMap, Column.KEEP_VALUE, false);
  1786. }
  1787. /**
  1788. * @return the generated RowId added to a row of values created via {@link
  1789. * #asRowWithRowId}
  1790. * @usage _intermediate_method_
  1791. */
  1792. public RowId getRowId(Object[] row) {
  1793. return (RowId)row[_columns.size()];
  1794. }
  1795. /**
  1796. * Converts a map of columnName -&gt; columnValue to an array of row values.
  1797. */
  1798. private Object[] asRow(Map<String,?> rowMap, Object defaultValue,
  1799. boolean returnRowId)
  1800. {
  1801. int len = _columns.size();
  1802. if(returnRowId) {
  1803. ++len;
  1804. }
  1805. Object[] row = new Object[len];
  1806. if(defaultValue != null) {
  1807. Arrays.fill(row, defaultValue);
  1808. }
  1809. if(returnRowId) {
  1810. row[len - 1] = ColumnImpl.RETURN_ROW_ID;
  1811. }
  1812. if(rowMap == null) {
  1813. return row;
  1814. }
  1815. for(ColumnImpl col : _columns) {
  1816. if(rowMap.containsKey(col.getName())) {
  1817. col.setRowValue(row, col.getRowValue(rowMap));
  1818. }
  1819. }
  1820. return row;
  1821. }
  1822. @Override
  1823. public Object[] addRow(Object... row) throws IOException {
  1824. return addRows(Collections.singletonList(row), false).get(0);
  1825. }
  1826. @Override
  1827. public <M extends Map<String,Object>> M addRowFromMap(M row)
  1828. throws IOException
  1829. {
  1830. Object[] rowValues = asRow(row);
  1831. addRow(rowValues);
  1832. returnRowValues(row, rowValues, _columns);
  1833. return row;
  1834. }
  1835. @Override
  1836. public List<? extends Object[]> addRows(List<? extends Object[]> rows)
  1837. throws IOException
  1838. {
  1839. return addRows(rows, true);
  1840. }
  1841. @Override
  1842. public <M extends Map<String,Object>> List<M> addRowsFromMaps(List<M> rows)
  1843. throws IOException
  1844. {
  1845. List<Object[]> rowValuesList = new ArrayList<Object[]>(rows.size());
  1846. for(Map<String,Object> row : rows) {
  1847. rowValuesList.add(asRow(row));
  1848. }
  1849. addRows(rowValuesList);
  1850. for(int i = 0; i < rowValuesList.size(); ++i) {
  1851. Map<String,Object> row = rows.get(i);
  1852. Object[] rowValues = rowValuesList.get(i);
  1853. returnRowValues(row, rowValues, _columns);
  1854. }
  1855. return rows;
  1856. }
  1857. private static void returnRowValues(Map<String,Object> row, Object[] rowValues,
  1858. List<ColumnImpl> cols)
  1859. {
  1860. for(ColumnImpl col : cols) {
  1861. col.setRowValue(row, col.getRowValue(rowValues));
  1862. }
  1863. }
  1864. /**
  1865. * Add multiple rows to this table, only writing to disk after all
  1866. * rows have been written, and every time a data page is filled.
  1867. * @param rows List of Object[] row values
  1868. */
  1869. protected List<? extends Object[]> addRows(List<? extends Object[]> rows,
  1870. final boolean isBatchWrite)
  1871. throws IOException
  1872. {
  1873. if(rows.isEmpty()) {
  1874. return rows;
  1875. }
  1876. getPageChannel().startWrite();
  1877. try {
  1878. ByteBuffer dataPage = null;
  1879. int pageNumber = PageChannel.INVALID_PAGE_NUMBER;
  1880. int updateCount = 0;
  1881. int autoNumAssignCount = 0;
  1882. WriteRowState writeRowState =
  1883. (!_autoNumColumns.isEmpty() ? new WriteRowState() : null);
  1884. try {
  1885. List<Object[]> dupeRows = null;
  1886. final int numCols = _columns.size();
  1887. for (int i = 0; i < rows.size(); i++) {
  1888. // we need to make sure the row is the right length and is an
  1889. // Object[] (fill with null if too short). note, if the row is
  1890. // copied the caller will not be able to access any generated
  1891. // auto-number value, but if they need that info they should use a
  1892. // row array of the right size/type!
  1893. Object[] row = rows.get(i);
  1894. if((row.length < numCols) || (row.getClass() != Object[].class)) {
  1895. row = dupeRow(row, numCols);
  1896. // copy the input rows to a modifiable list so we can update the
  1897. // elements
  1898. if(dupeRows == null) {
  1899. dupeRows = new ArrayList<Object[]>(rows);
  1900. rows = dupeRows;
  1901. }
  1902. // we copied the row, so put the copy back into the rows list
  1903. dupeRows.set(i, row);
  1904. }
  1905. // handle various value massaging activities
  1906. for(ColumnImpl column : _columns) {
  1907. if(!column.isAutoNumber()) {
  1908. Object val = column.getRowValue(row);
  1909. if(val == null) {
  1910. val = column.generateDefaultValue();
  1911. }
  1912. // pass input value through column validator
  1913. column.setRowValue(row, column.validate(val));
  1914. }
  1915. }
  1916. // fill in autonumbers
  1917. handleAutoNumbersForAdd(row, writeRowState);
  1918. ++autoNumAssignCount;
  1919. // need to assign calculated values after all the other fields are
  1920. // filled in but before final validation
  1921. _calcColEval.calculate(row);
  1922. // run row validation if enabled
  1923. if(_rowValidator != null) {
  1924. _rowValidator.validate(row);
  1925. }
  1926. // write the row of data to a temporary buffer
  1927. ByteBuffer rowData = createRow(
  1928. row, _writeRowBufferH.getPageBuffer(getPageChannel()));
  1929. int rowSize = rowData.remaining();
  1930. if (rowSize > getFormat().MAX_ROW_SIZE) {
  1931. throw new InvalidValueException(withErrorContext(
  1932. "Row size " + rowSize + " is too large"));
  1933. }
  1934. // get page with space
  1935. dataPage = findFreeRowSpace(rowSize, dataPage, pageNumber);
  1936. pageNumber = _addRowBufferH.getPageNumber();
  1937. // determine where this row will end up on the page
  1938. int rowNum = getRowsOnDataPage(dataPage, getFormat());
  1939. RowIdImpl rowId = new RowIdImpl(pageNumber, rowNum);
  1940. // before we actually write the row data, we verify all the database
  1941. // constraints.
  1942. if(!_indexDatas.isEmpty()) {
  1943. IndexData.PendingChange idxChange = null;
  1944. try {
  1945. // handle foreign keys before adding to table
  1946. _fkEnforcer.addRow(row);
  1947. // prepare index updates
  1948. for(IndexData indexData : _indexDatas) {
  1949. idxChange = indexData.prepareAddRow(row, rowId, idxChange);
  1950. }
  1951. // complete index updates
  1952. IndexData.commitAll(idxChange);
  1953. } catch(ConstraintViolationException ce) {
  1954. IndexData.rollbackAll(idxChange);
  1955. throw ce;
  1956. }
  1957. }
  1958. // we have satisfied all the constraints, write the row
  1959. addDataPageRow(dataPage, rowSize, getFormat(), 0);
  1960. dataPage.put(rowData);
  1961. // return rowTd if desired
  1962. if((row.length > numCols) &&
  1963. (row[numCols] == ColumnImpl.RETURN_ROW_ID)) {
  1964. row[numCols] = rowId;
  1965. }
  1966. ++updateCount;
  1967. }
  1968. writeDataPage(dataPage, pageNumber);
  1969. // Update tdef page
  1970. updateTableDefinition(rows.size());
  1971. } catch(Exception rowWriteFailure) {
  1972. boolean isWriteFailure = isWriteFailure(rowWriteFailure);
  1973. if(!isWriteFailure && (autoNumAssignCount > updateCount)) {
  1974. // we assigned some autonumbers which won't get written. attempt to
  1975. // recover them so we don't get ugly "holes"
  1976. restoreAutoNumbersFromAdd(rows.get(autoNumAssignCount - 1));
  1977. }
  1978. if(!isBatchWrite) {
  1979. // just re-throw the original exception
  1980. if(rowWriteFailure instanceof IOException) {
  1981. throw (IOException)rowWriteFailure;
  1982. }
  1983. throw (RuntimeException)rowWriteFailure;
  1984. }
  1985. // attempt to resolve a partial batch write
  1986. if(isWriteFailure) {
  1987. // we don't really know the status of any of the rows, so clear the
  1988. // update count
  1989. updateCount = 0;
  1990. } else if(updateCount > 0) {
  1991. // attempt to flush the rows already written to disk
  1992. try {
  1993. writeDataPage(dataPage, pageNumber);
  1994. // Update tdef page
  1995. updateTableDefinition(updateCount);
  1996. } catch(Exception flushFailure) {
  1997. // the flush failure is "worse" as it implies possible database
  1998. // corruption (failed write vs. a row failure which was not a
  1999. // write failure). we don't know the status of any rows at this
  2000. // point (and the original failure is probably irrelevant)
  2001. LOG.warn(withErrorContext(
  2002. "Secondary row failure which preceded the write failure"),
  2003. rowWriteFailure);
  2004. updateCount = 0;
  2005. rowWriteFailure = flushFailure;
  2006. }
  2007. }
  2008. throw new BatchUpdateException(
  2009. updateCount, withErrorContext("Failed adding rows"),
  2010. rowWriteFailure);
  2011. }
  2012. } finally {
  2013. getPageChannel().finishWrite();
  2014. }
  2015. return rows;
  2016. }
  2017. private static boolean isWriteFailure(Throwable t) {
  2018. while(t != null) {
  2019. if((t instanceof IOException) && !(t instanceof JackcessException)) {
  2020. return true;
  2021. }
  2022. t = t.getCause();
  2023. }
  2024. // some other sort of exception which is not a write failure
  2025. return false;
  2026. }
  2027. @Override
  2028. public Row updateRow(Row row) throws IOException {
  2029. return updateRowFromMap(
  2030. getDefaultCursor().getRowState(), (RowIdImpl)row.getId(), row);
  2031. }
  2032. /**
  2033. * Update the row with the given id. Provided RowId must have previously
  2034. * been returned from this Table.
  2035. * @return the given row, updated with the current row values
  2036. * @throws IllegalStateException if the given row is not valid, or deleted.
  2037. * @usage _intermediate_method_
  2038. */
  2039. public Object[] updateRow(RowId rowId, Object... row) throws IOException {
  2040. return updateRow(
  2041. getDefaultCursor().getRowState(), (RowIdImpl)rowId, row);
  2042. }
  2043. /**
  2044. * Update the given column's value for the given row id. Provided RowId
  2045. * must have previously been returned from this Table.
  2046. * @throws IllegalStateException if the given row is not valid, or deleted.
  2047. * @usage _intermediate_method_
  2048. */
  2049. public void updateValue(Column column, RowId rowId, Object value)
  2050. throws IOException
  2051. {
  2052. Object[] row = new Object[_columns.size()];
  2053. Arrays.fill(row, Column.KEEP_VALUE);
  2054. column.setRowValue(row, value);
  2055. updateRow(rowId, row);
  2056. }
  2057. public <M extends Map<String,Object>> M updateRowFromMap(
  2058. RowState rowState, RowIdImpl rowId, M row)
  2059. throws IOException
  2060. {
  2061. Object[] rowValues = updateRow(rowState, rowId, asUpdateRow(row));
  2062. returnRowValues(row, rowValues, _columns);
  2063. return row;
  2064. }
  2065. /**
  2066. * Update the row for the given rowId.
  2067. * @usage _advanced_method_
  2068. */
  2069. public Object[] updateRow(RowState rowState, RowIdImpl rowId, Object... row)
  2070. throws IOException
  2071. {
  2072. requireValidRowId(rowId);
  2073. getPageChannel().startWrite();
  2074. try {
  2075. // ensure that the relevant row state is up-to-date
  2076. ByteBuffer rowBuffer = positionAtRowData(rowState, rowId);
  2077. int oldRowSize = rowBuffer.remaining();
  2078. requireNonDeletedRow(rowState, rowId);
  2079. // we need to make sure the row is the right length & type (fill with
  2080. // null if too short).
  2081. if((row.length < _columns.size()) || (row.getClass() != Object[].class)) {
  2082. row = dupeRow(row, _columns.size());
  2083. }
  2084. // hang on to the raw values of var length columns we are "keeping". this
  2085. // will allow us to re-use pre-written var length data, which can save
  2086. // space for things like long value columns.
  2087. Map<ColumnImpl,byte[]> keepRawVarValues =
  2088. (!_varColumns.isEmpty() ? new HashMap<ColumnImpl,byte[]>() : null);
  2089. // handle various value massaging activities
  2090. for(ColumnImpl column : _columns) {
  2091. if(column.isAutoNumber()) {
  2092. // handle these separately (below)
  2093. continue;
  2094. }
  2095. Object rowValue = column.getRowValue(row);
  2096. if(rowValue == Column.KEEP_VALUE) {
  2097. // fill in any "keep value" fields (restore old value)
  2098. rowValue = getRowColumn(getFormat(), rowBuffer, column, rowState,
  2099. keepRawVarValues);
  2100. } else {
  2101. // set oldValue to something that could not possibly be a real value
  2102. Object oldValue = Column.KEEP_VALUE;
  2103. if(_indexColumns.contains(column)) {
  2104. // read (old) row value to help update indexes
  2105. oldValue = getRowColumn(getFormat(), rowBuffer, column, rowState,
  2106. null);
  2107. } else {
  2108. oldValue = rowState.getRowCacheValue(column.getColumnIndex());
  2109. }
  2110. // if the old value was passed back in, we don't need to validate
  2111. if(oldValue != rowValue) {
  2112. // pass input value through column validator
  2113. rowValue = column.validate(rowValue);
  2114. }
  2115. }
  2116. column.setRowValue(row, rowValue);
  2117. }
  2118. // fill in autonumbers
  2119. handleAutoNumbersForUpdate(row, rowBuffer, rowState);
  2120. // need to assign calculated values after all the other fields are
  2121. // filled in but before final validation
  2122. _calcColEval.calculate(row);
  2123. // run row validation if enabled
  2124. if(_rowValidator != null) {
  2125. _rowValidator.validate(row);
  2126. }
  2127. // generate new row bytes
  2128. ByteBuffer newRowData = createRow(
  2129. row, _writeRowBufferH.getPageBuffer(getPageChannel()), oldRowSize,
  2130. keepRawVarValues);
  2131. if (newRowData.limit() > getFormat().MAX_ROW_SIZE) {
  2132. throw new InvalidValueException(withErrorContext(
  2133. "Row size " + newRowData.limit() + " is too large"));
  2134. }
  2135. if(!_indexDatas.isEmpty()) {
  2136. IndexData.PendingChange idxChange = null;
  2137. try {
  2138. Object[] oldRowValues = rowState.getRowCacheValues();
  2139. // check foreign keys before actually updating
  2140. _fkEnforcer.updateRow(oldRowValues, row);
  2141. // prepare index updates
  2142. for(IndexData indexData : _indexDatas) {
  2143. idxChange = indexData.prepareUpdateRow(oldRowValues, rowId, row,
  2144. idxChange);
  2145. }
  2146. // complete index updates
  2147. IndexData.commitAll(idxChange);
  2148. } catch(ConstraintViolationException ce) {
  2149. IndexData.rollbackAll(idxChange);
  2150. throw ce;
  2151. }
  2152. }
  2153. // see if we can squeeze the new row data into the existing row
  2154. rowBuffer.reset();
  2155. int rowSize = newRowData.remaining();
  2156. ByteBuffer dataPage = null;
  2157. int pageNumber = PageChannel.INVALID_PAGE_NUMBER;
  2158. if(oldRowSize >= rowSize) {
  2159. // awesome, slap it in!
  2160. rowBuffer.put(newRowData);
  2161. // grab the page we just updated
  2162. dataPage = rowState.getFinalPage();
  2163. pageNumber = rowState.getFinalRowId().getPageNumber();
  2164. } else {
  2165. // bummer, need to find a new page for the data
  2166. dataPage = findFreeRowSpace(rowSize, null,
  2167. PageChannel.INVALID_PAGE_NUMBER);
  2168. pageNumber = _addRowBufferH.getPageNumber();
  2169. RowIdImpl headerRowId = rowState.getHeaderRowId();
  2170. ByteBuffer headerPage = rowState.getHeaderPage();
  2171. if(pageNumber == headerRowId.getPageNumber()) {
  2172. // new row is on the same page as header row, share page
  2173. dataPage = headerPage;
  2174. }
  2175. // write out the new row data (set the deleted flag on the new data row
  2176. // so that it is ignored during normal table traversal)
  2177. int rowNum = addDataPageRow(dataPage, rowSize, getFormat(),
  2178. DELETED_ROW_MASK);
  2179. dataPage.put(newRowData);
  2180. // write the overflow info into the header row and clear out the
  2181. // remaining header data
  2182. rowBuffer = PageChannel.narrowBuffer(
  2183. headerPage,
  2184. findRowStart(headerPage, headerRowId.getRowNumber(), getFormat()),
  2185. findRowEnd(headerPage, headerRowId.getRowNumber(), getFormat()));
  2186. rowBuffer.put((byte)rowNum);
  2187. ByteUtil.put3ByteInt(rowBuffer, pageNumber);
  2188. ByteUtil.clearRemaining(rowBuffer);
  2189. // set the overflow flag on the header row
  2190. int headerRowIndex = getRowStartOffset(headerRowId.getRowNumber(),
  2191. getFormat());
  2192. headerPage.putShort(headerRowIndex,
  2193. (short)(headerPage.getShort(headerRowIndex)
  2194. | OVERFLOW_ROW_MASK));
  2195. if(pageNumber != headerRowId.getPageNumber()) {
  2196. writeDataPage(headerPage, headerRowId.getPageNumber());
  2197. }
  2198. }
  2199. writeDataPage(dataPage, pageNumber);
  2200. updateTableDefinition(0);
  2201. } finally {
  2202. getPageChannel().finishWrite();
  2203. }
  2204. return row;
  2205. }
  2206. private ByteBuffer findFreeRowSpace(int rowSize, ByteBuffer dataPage,
  2207. int pageNumber)
  2208. throws IOException
  2209. {
  2210. // assume incoming page is modified
  2211. boolean modifiedPage = true;
  2212. if(dataPage == null) {
  2213. // find owned page w/ free space
  2214. dataPage = findFreeRowSpace(_ownedPages, _freeSpacePages,
  2215. _addRowBufferH);
  2216. if(dataPage == null) {
  2217. // No data pages exist (with free space). Create a new one.
  2218. return newDataPage();
  2219. }
  2220. // found a page, see if it will work
  2221. pageNumber = _addRowBufferH.getPageNumber();
  2222. // since we just loaded this page, it is not yet modified
  2223. modifiedPage = false;
  2224. }
  2225. if(!rowFitsOnDataPage(rowSize, dataPage, getFormat())) {
  2226. // Last data page is full. Write old one and create a new one.
  2227. if(modifiedPage) {
  2228. writeDataPage(dataPage, pageNumber);
  2229. }
  2230. _freeSpacePages.removePageNumber(pageNumber);
  2231. dataPage = newDataPage();
  2232. }
  2233. return dataPage;
  2234. }
  2235. static ByteBuffer findFreeRowSpace(
  2236. UsageMap ownedPages, UsageMap freeSpacePages,
  2237. TempPageHolder rowBufferH)
  2238. throws IOException
  2239. {
  2240. // find last data page (Not bothering to check other pages for free
  2241. // space.)
  2242. UsageMap.PageCursor revPageCursor = ownedPages.cursor();
  2243. revPageCursor.afterLast();
  2244. while(true) {
  2245. int tmpPageNumber = revPageCursor.getPreviousPage();
  2246. if(tmpPageNumber < 0) {
  2247. break;
  2248. }
  2249. // only use if actually listed in free space pages
  2250. if(!freeSpacePages.containsPageNumber(tmpPageNumber)) {
  2251. continue;
  2252. }
  2253. ByteBuffer dataPage = rowBufferH.setPage(ownedPages.getPageChannel(),
  2254. tmpPageNumber);
  2255. if(dataPage.get() == PageTypes.DATA) {
  2256. // found last data page with free space
  2257. return dataPage;
  2258. }
  2259. }
  2260. return null;
  2261. }
  2262. /**
  2263. * Updates the table definition after rows are modified.
  2264. */
  2265. private void updateTableDefinition(int rowCountInc) throws IOException
  2266. {
  2267. // load table definition
  2268. ByteBuffer tdefPage = _tableDefBufferH.setPage(getPageChannel(),
  2269. _tableDefPageNumber);
  2270. // make sure rowcount and autonumber are up-to-date
  2271. _rowCount += rowCountInc;
  2272. tdefPage.putInt(getFormat().OFFSET_NUM_ROWS, _rowCount);
  2273. tdefPage.putInt(getFormat().OFFSET_NEXT_AUTO_NUMBER, _lastLongAutoNumber);
  2274. int ctypeOff = getFormat().OFFSET_NEXT_COMPLEX_AUTO_NUMBER;
  2275. if(ctypeOff >= 0) {
  2276. tdefPage.putInt(ctypeOff, _lastComplexTypeAutoNumber);
  2277. }
  2278. // write any index changes
  2279. for (IndexData indexData : _indexDatas) {
  2280. // write the unique entry count for the index to the table definition
  2281. // page
  2282. tdefPage.putInt(indexData.getUniqueEntryCountOffset(),
  2283. indexData.getUniqueEntryCount());
  2284. // write the entry page for the index
  2285. indexData.update();
  2286. }
  2287. // write modified table definition
  2288. getPageChannel().writePage(tdefPage, _tableDefPageNumber);
  2289. }
  2290. /**
  2291. * Create a new data page
  2292. * @return Page number of the new page
  2293. */
  2294. private ByteBuffer newDataPage() throws IOException {
  2295. ByteBuffer dataPage = _addRowBufferH.setNewPage(getPageChannel());
  2296. dataPage.put(PageTypes.DATA); //Page type
  2297. dataPage.put((byte) 1); //Unknown
  2298. dataPage.putShort((short)getFormat().DATA_PAGE_INITIAL_FREE_SPACE); //Free space in this page
  2299. dataPage.putInt(_tableDefPageNumber); //Page pointer to table definition
  2300. dataPage.putInt(0); //Unknown
  2301. dataPage.putShort((short)0); //Number of rows on this page
  2302. int pageNumber = _addRowBufferH.getPageNumber();
  2303. getPageChannel().writePage(dataPage, pageNumber);
  2304. _ownedPages.addPageNumber(pageNumber);
  2305. _freeSpacePages.addPageNumber(pageNumber);
  2306. return dataPage;
  2307. }
  2308. // exposed for unit tests
  2309. protected ByteBuffer createRow(Object[] rowArray, ByteBuffer buffer)
  2310. throws IOException
  2311. {
  2312. return createRow(rowArray, buffer, 0,
  2313. Collections.<ColumnImpl,byte[]>emptyMap());
  2314. }
  2315. /**
  2316. * Serialize a row of Objects into a byte buffer.
  2317. *
  2318. * @param rowArray row data, expected to be correct length for this table
  2319. * @param buffer buffer to which to write the row data
  2320. * @param minRowSize min size for result row
  2321. * @param rawVarValues optional, pre-written values for var length columns
  2322. * (enables re-use of previously written values).
  2323. * @return the given buffer, filled with the row data
  2324. */
  2325. private ByteBuffer createRow(Object[] rowArray, ByteBuffer buffer,
  2326. int minRowSize,
  2327. Map<ColumnImpl,byte[]> rawVarValues)
  2328. throws IOException
  2329. {
  2330. buffer.putShort(_maxColumnCount);
  2331. NullMask nullMask = new NullMask(_maxColumnCount);
  2332. //Fixed length column data comes first
  2333. int fixedDataStart = buffer.position();
  2334. int fixedDataEnd = fixedDataStart;
  2335. for (ColumnImpl col : _columns) {
  2336. if(col.isVariableLength()) {
  2337. continue;
  2338. }
  2339. Object rowValue = col.getRowValue(rowArray);
  2340. if (col.storeInNullMask()) {
  2341. if(col.writeToNullMask(rowValue)) {
  2342. nullMask.markNotNull(col);
  2343. }
  2344. rowValue = null;
  2345. }
  2346. if(rowValue != null) {
  2347. // we have a value to write
  2348. nullMask.markNotNull(col);
  2349. // remainingRowLength is ignored when writing fixed length data
  2350. buffer.position(fixedDataStart + col.getFixedDataOffset());
  2351. buffer.put(col.write(rowValue, 0));
  2352. }
  2353. // always insert space for the entire fixed data column length
  2354. // (including null values), access expects the row to always be at least
  2355. // big enough to hold all fixed values
  2356. buffer.position(fixedDataStart + col.getFixedDataOffset() +
  2357. col.getLength());
  2358. // keep track of the end of fixed data
  2359. if(buffer.position() > fixedDataEnd) {
  2360. fixedDataEnd = buffer.position();
  2361. }
  2362. }
  2363. // reposition at end of fixed data
  2364. buffer.position(fixedDataEnd);
  2365. // only need this info if this table contains any var length data
  2366. if(_maxVarColumnCount > 0) {
  2367. int maxRowSize = getFormat().MAX_ROW_SIZE;
  2368. // figure out how much space remains for var length data. first,
  2369. // account for already written space
  2370. maxRowSize -= buffer.position();
  2371. // now, account for trailer space
  2372. int trailerSize = (nullMask.byteSize() + 4 + (_maxVarColumnCount * 2));
  2373. maxRowSize -= trailerSize;
  2374. // for each non-null long value column we need to reserve a small
  2375. // amount of space so that we don't end up running out of row space
  2376. // later by being too greedy
  2377. for (ColumnImpl varCol : _varColumns) {
  2378. if((varCol.getType().isLongValue()) &&
  2379. (varCol.getRowValue(rowArray) != null)) {
  2380. maxRowSize -= getFormat().SIZE_LONG_VALUE_DEF;
  2381. }
  2382. }
  2383. //Now write out variable length column data
  2384. short[] varColumnOffsets = new short[_maxVarColumnCount];
  2385. int varColumnOffsetsIndex = 0;
  2386. for (ColumnImpl varCol : _varColumns) {
  2387. short offset = (short) buffer.position();
  2388. Object rowValue = varCol.getRowValue(rowArray);
  2389. if (rowValue != null) {
  2390. // we have a value
  2391. nullMask.markNotNull(varCol);
  2392. byte[] rawValue = null;
  2393. ByteBuffer varDataBuf = null;
  2394. if(((rawValue = rawVarValues.get(varCol)) != null) &&
  2395. (rawValue.length <= maxRowSize)) {
  2396. // save time and potentially db space, re-use raw value
  2397. varDataBuf = ByteBuffer.wrap(rawValue);
  2398. } else {
  2399. // write column value
  2400. varDataBuf = varCol.write(rowValue, maxRowSize);
  2401. }
  2402. maxRowSize -= varDataBuf.remaining();
  2403. if(varCol.getType().isLongValue()) {
  2404. // we already accounted for some amount of the long value data
  2405. // above. add that space back so we don't double count
  2406. maxRowSize += getFormat().SIZE_LONG_VALUE_DEF;
  2407. }
  2408. try {
  2409. buffer.put(varDataBuf);
  2410. } catch(BufferOverflowException e) {
  2411. // if the data is too big for the buffer, then we have gone over
  2412. // the max row size
  2413. throw new InvalidValueException(withErrorContext(
  2414. "Row size " + buffer.limit() + " is too large"));
  2415. }
  2416. }
  2417. // we do a loop here so that we fill in offsets for deleted columns
  2418. while(varColumnOffsetsIndex <= varCol.getVarLenTableIndex()) {
  2419. varColumnOffsets[varColumnOffsetsIndex++] = offset;
  2420. }
  2421. }
  2422. // fill in offsets for any remaining deleted columns
  2423. while(varColumnOffsetsIndex < varColumnOffsets.length) {
  2424. varColumnOffsets[varColumnOffsetsIndex++] = (short) buffer.position();
  2425. }
  2426. // record where we stopped writing
  2427. int eod = buffer.position();
  2428. // insert padding if necessary
  2429. padRowBuffer(buffer, minRowSize, trailerSize);
  2430. buffer.putShort((short) eod); //EOD marker
  2431. //Now write out variable length offsets
  2432. //Offsets are stored in reverse order
  2433. for (int i = _maxVarColumnCount - 1; i >= 0; i--) {
  2434. buffer.putShort(varColumnOffsets[i]);
  2435. }
  2436. buffer.putShort(_maxVarColumnCount); //Number of var length columns
  2437. } else {
  2438. // insert padding for row w/ no var cols
  2439. padRowBuffer(buffer, minRowSize, nullMask.byteSize());
  2440. }
  2441. nullMask.write(buffer); //Null mask
  2442. buffer.flip();
  2443. return buffer;
  2444. }
  2445. /**
  2446. * Fill in all autonumber column values for add.
  2447. */
  2448. private void handleAutoNumbersForAdd(Object[] row, WriteRowState writeRowState)
  2449. throws IOException
  2450. {
  2451. if(_autoNumColumns.isEmpty()) {
  2452. return;
  2453. }
  2454. boolean enableInsert = isAllowAutoNumberInsert();
  2455. writeRowState.resetAutoNumber();
  2456. for(ColumnImpl col : _autoNumColumns) {
  2457. // ignore input row value, use original row value (unless explicitly
  2458. // enabled)
  2459. Object inRowValue = getInputAutoNumberRowValue(enableInsert, col, row);
  2460. ColumnImpl.AutoNumberGenerator autoNumGen = col.getAutoNumberGenerator();
  2461. Object rowValue = ((inRowValue == null) ?
  2462. autoNumGen.getNext(writeRowState) :
  2463. autoNumGen.handleInsert(writeRowState, inRowValue));
  2464. col.setRowValue(row, rowValue);
  2465. }
  2466. }
  2467. /**
  2468. * Fill in all autonumber column values for update.
  2469. */
  2470. private void handleAutoNumbersForUpdate(Object[] row, ByteBuffer rowBuffer,
  2471. RowState rowState)
  2472. throws IOException
  2473. {
  2474. if(_autoNumColumns.isEmpty()) {
  2475. return;
  2476. }
  2477. boolean enableInsert = isAllowAutoNumberInsert();
  2478. rowState.resetAutoNumber();
  2479. for(ColumnImpl col : _autoNumColumns) {
  2480. // ignore input row value, use original row value (unless explicitly
  2481. // enabled)
  2482. Object inRowValue = getInputAutoNumberRowValue(enableInsert, col, row);
  2483. Object rowValue =
  2484. ((inRowValue == null) ?
  2485. getRowColumn(getFormat(), rowBuffer, col, rowState, null) :
  2486. col.getAutoNumberGenerator().handleInsert(rowState, inRowValue));
  2487. col.setRowValue(row, rowValue);
  2488. }
  2489. }
  2490. /**
  2491. * Optionally get the input autonumber row value for the given column from
  2492. * the given row if one was provided.
  2493. */
  2494. private static Object getInputAutoNumberRowValue(
  2495. boolean enableInsert, ColumnImpl col, Object[] row)
  2496. {
  2497. if(!enableInsert) {
  2498. return null;
  2499. }
  2500. Object inRowValue = col.getRowValue(row);
  2501. if((inRowValue == Column.KEEP_VALUE) || (inRowValue == Column.AUTO_NUMBER)) {
  2502. // these "special" values both behave like nothing was given
  2503. inRowValue = null;
  2504. }
  2505. return inRowValue;
  2506. }
  2507. /**
  2508. * Restores all autonumber column values from a failed add row.
  2509. */
  2510. private void restoreAutoNumbersFromAdd(Object[] row)
  2511. throws IOException
  2512. {
  2513. if(_autoNumColumns.isEmpty()) {
  2514. return;
  2515. }
  2516. for(ColumnImpl col : _autoNumColumns) {
  2517. // restore the last value from the row
  2518. col.getAutoNumberGenerator().restoreLast(col.getRowValue(row));
  2519. }
  2520. }
  2521. private static void padRowBuffer(ByteBuffer buffer, int minRowSize,
  2522. int trailerSize)
  2523. {
  2524. int pos = buffer.position();
  2525. if((pos + trailerSize) < minRowSize) {
  2526. // pad the row to get to the min byte size
  2527. int padSize = minRowSize - (pos + trailerSize);
  2528. ByteUtil.clearRange(buffer, pos, pos + padSize);
  2529. ByteUtil.forward(buffer, padSize);
  2530. }
  2531. }
  2532. @Override
  2533. public int getRowCount() {
  2534. return _rowCount;
  2535. }
  2536. int getNextLongAutoNumber() {
  2537. // note, the saved value is the last one handed out, so pre-increment
  2538. return ++_lastLongAutoNumber;
  2539. }
  2540. int getLastLongAutoNumber() {
  2541. // gets the last used auto number (does not modify)
  2542. return _lastLongAutoNumber;
  2543. }
  2544. void adjustLongAutoNumber(int inLongAutoNumber) {
  2545. if(inLongAutoNumber > _lastLongAutoNumber) {
  2546. _lastLongAutoNumber = inLongAutoNumber;
  2547. }
  2548. }
  2549. void restoreLastLongAutoNumber(int lastLongAutoNumber) {
  2550. // restores the last used auto number
  2551. _lastLongAutoNumber = lastLongAutoNumber - 1;
  2552. }
  2553. int getNextComplexTypeAutoNumber() {
  2554. // note, the saved value is the last one handed out, so pre-increment
  2555. return ++_lastComplexTypeAutoNumber;
  2556. }
  2557. int getLastComplexTypeAutoNumber() {
  2558. // gets the last used auto number (does not modify)
  2559. return _lastComplexTypeAutoNumber;
  2560. }
  2561. void adjustComplexTypeAutoNumber(int inComplexTypeAutoNumber) {
  2562. if(inComplexTypeAutoNumber > _lastComplexTypeAutoNumber) {
  2563. _lastComplexTypeAutoNumber = inComplexTypeAutoNumber;
  2564. }
  2565. }
  2566. void restoreLastComplexTypeAutoNumber(int lastComplexTypeAutoNumber) {
  2567. // restores the last used auto number
  2568. _lastComplexTypeAutoNumber = lastComplexTypeAutoNumber - 1;
  2569. }
  2570. @Override
  2571. public String toString() {
  2572. return CustomToStringStyle.builder(this)
  2573. .append("type", (_tableType + (!isSystem() ? " (USER)" : " (SYSTEM)")))
  2574. .append("name", _name)
  2575. .append("rowCount", _rowCount)
  2576. .append("columnCount", _columns.size())
  2577. .append("indexCount(data)", _indexCount)
  2578. .append("logicalIndexCount", _logicalIndexCount)
  2579. .append("validator", CustomToStringStyle.ignoreNull(_rowValidator))
  2580. .append("columns", _columns)
  2581. .append("indexes", _indexes)
  2582. .append("ownedPages", _ownedPages)
  2583. .toString();
  2584. }
  2585. /**
  2586. * @return A simple String representation of the entire table in
  2587. * tab-delimited format
  2588. * @usage _general_method_
  2589. */
  2590. public String display() throws IOException {
  2591. return display(Long.MAX_VALUE);
  2592. }
  2593. /**
  2594. * @param limit Maximum number of rows to display
  2595. * @return A simple String representation of the entire table in
  2596. * tab-delimited format
  2597. * @usage _general_method_
  2598. */
  2599. public String display(long limit) throws IOException {
  2600. reset();
  2601. StringWriter rtn = new StringWriter();
  2602. new ExportUtil.Builder(getDefaultCursor()).setDelimiter("\t").setHeader(true)
  2603. .exportWriter(new BufferedWriter(rtn));
  2604. return rtn.toString();
  2605. }
  2606. /**
  2607. * Updates free space and row info for a new row of the given size in the
  2608. * given data page. Positions the page for writing the row data.
  2609. * @return the row number of the new row
  2610. * @usage _advanced_method_
  2611. */
  2612. public static int addDataPageRow(ByteBuffer dataPage,
  2613. int rowSize,
  2614. JetFormat format,
  2615. int rowFlags)
  2616. {
  2617. int rowSpaceUsage = getRowSpaceUsage(rowSize, format);
  2618. // Decrease free space record.
  2619. short freeSpaceInPage = dataPage.getShort(format.OFFSET_FREE_SPACE);
  2620. dataPage.putShort(format.OFFSET_FREE_SPACE, (short) (freeSpaceInPage -
  2621. rowSpaceUsage));
  2622. // Increment row count record.
  2623. short rowCount = dataPage.getShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE);
  2624. dataPage.putShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE,
  2625. (short) (rowCount + 1));
  2626. // determine row position
  2627. short rowLocation = findRowEnd(dataPage, rowCount, format);
  2628. rowLocation -= rowSize;
  2629. // write row position
  2630. dataPage.putShort(getRowStartOffset(rowCount, format),
  2631. (short)(rowLocation | rowFlags));
  2632. // set position for row data
  2633. dataPage.position(rowLocation);
  2634. return rowCount;
  2635. }
  2636. /**
  2637. * Returns the row count for the current page. If the page is invalid
  2638. * ({@code null}) or the page is not a DATA page, 0 is returned.
  2639. */
  2640. static int getRowsOnDataPage(ByteBuffer rowBuffer, JetFormat format)
  2641. throws IOException
  2642. {
  2643. int rowsOnPage = 0;
  2644. if((rowBuffer != null) && (rowBuffer.get(0) == PageTypes.DATA)) {
  2645. rowsOnPage = rowBuffer.getShort(format.OFFSET_NUM_ROWS_ON_DATA_PAGE);
  2646. }
  2647. return rowsOnPage;
  2648. }
  2649. /**
  2650. * @throws IllegalStateException if the given rowId is invalid
  2651. */
  2652. private void requireValidRowId(RowIdImpl rowId) {
  2653. if(!rowId.isValid()) {
  2654. throw new IllegalArgumentException(withErrorContext(
  2655. "Given rowId is invalid: " + rowId));
  2656. }
  2657. }
  2658. /**
  2659. * @throws IllegalStateException if the given row is invalid or deleted
  2660. */
  2661. private void requireNonDeletedRow(RowState rowState, RowIdImpl rowId)
  2662. {
  2663. if(!rowState.isValid()) {
  2664. throw new IllegalArgumentException(withErrorContext(
  2665. "Given rowId is invalid for this table: " + rowId));
  2666. }
  2667. if(rowState.isDeleted()) {
  2668. throw new IllegalStateException(withErrorContext(
  2669. "Row is deleted: " + rowId));
  2670. }
  2671. }
  2672. /**
  2673. * @usage _advanced_method_
  2674. */
  2675. public static boolean isDeletedRow(short rowStart) {
  2676. return ((rowStart & DELETED_ROW_MASK) != 0);
  2677. }
  2678. /**
  2679. * @usage _advanced_method_
  2680. */
  2681. public static boolean isOverflowRow(short rowStart) {
  2682. return ((rowStart & OVERFLOW_ROW_MASK) != 0);
  2683. }
  2684. /**
  2685. * @usage _advanced_method_
  2686. */
  2687. public static short cleanRowStart(short rowStart) {
  2688. return (short)(rowStart & OFFSET_MASK);
  2689. }
  2690. /**
  2691. * @usage _advanced_method_
  2692. */
  2693. public static short findRowStart(ByteBuffer buffer, int rowNum,
  2694. JetFormat format)
  2695. {
  2696. return cleanRowStart(
  2697. buffer.getShort(getRowStartOffset(rowNum, format)));
  2698. }
  2699. /**
  2700. * @usage _advanced_method_
  2701. */
  2702. public static int getRowStartOffset(int rowNum, JetFormat format)
  2703. {
  2704. return format.OFFSET_ROW_START + (format.SIZE_ROW_LOCATION * rowNum);
  2705. }
  2706. /**
  2707. * @usage _advanced_method_
  2708. */
  2709. public static short findRowEnd(ByteBuffer buffer, int rowNum,
  2710. JetFormat format)
  2711. {
  2712. return (short)((rowNum == 0) ?
  2713. format.PAGE_SIZE :
  2714. cleanRowStart(
  2715. buffer.getShort(getRowEndOffset(rowNum, format))));
  2716. }
  2717. /**
  2718. * @usage _advanced_method_
  2719. */
  2720. public static int getRowEndOffset(int rowNum, JetFormat format)
  2721. {
  2722. return format.OFFSET_ROW_START + (format.SIZE_ROW_LOCATION * (rowNum - 1));
  2723. }
  2724. /**
  2725. * @usage _advanced_method_
  2726. */
  2727. public static int getRowSpaceUsage(int rowSize, JetFormat format)
  2728. {
  2729. return rowSize + format.SIZE_ROW_LOCATION;
  2730. }
  2731. private void initAutoNumberColumns() {
  2732. for(ColumnImpl c : _columns) {
  2733. if(c.isAutoNumber()) {
  2734. _autoNumColumns.add(c);
  2735. }
  2736. }
  2737. }
  2738. private void initCalculatedColumns() {
  2739. for(ColumnImpl c : _columns) {
  2740. if(c.isCalculated()) {
  2741. _calcColEval.add(c);
  2742. }
  2743. }
  2744. }
  2745. boolean isThisTable(Identifier identifier) {
  2746. String collectionName = identifier.getCollectionName();
  2747. return ((collectionName == null) ||
  2748. collectionName.equalsIgnoreCase(getName()));
  2749. }
  2750. /**
  2751. * Returns {@code true} if a row of the given size will fit on the given
  2752. * data page, {@code false} otherwise.
  2753. * @usage _advanced_method_
  2754. */
  2755. public static boolean rowFitsOnDataPage(
  2756. int rowLength, ByteBuffer dataPage, JetFormat format)
  2757. throws IOException
  2758. {
  2759. int rowSpaceUsage = getRowSpaceUsage(rowLength, format);
  2760. short freeSpaceInPage = dataPage.getShort(format.OFFSET_FREE_SPACE);
  2761. int rowsOnPage = getRowsOnDataPage(dataPage, format);
  2762. return ((rowSpaceUsage <= freeSpaceInPage) &&
  2763. (rowsOnPage < format.MAX_NUM_ROWS_ON_DATA_PAGE));
  2764. }
  2765. /**
  2766. * Duplicates and returns a row of data, optionally with a longer length
  2767. * filled with {@code null}.
  2768. */
  2769. static Object[] dupeRow(Object[] row, int newRowLength) {
  2770. Object[] copy = new Object[newRowLength];
  2771. System.arraycopy(row, 0, copy, 0, Math.min(row.length, newRowLength));
  2772. return copy;
  2773. }
  2774. String withErrorContext(String msg) {
  2775. return withErrorContext(msg, getDatabase(), getName());
  2776. }
  2777. private static String withErrorContext(String msg, DatabaseImpl db,
  2778. String tableName) {
  2779. return msg + " (Db=" + db.getName() + ";Table=" + tableName + ")";
  2780. }
  2781. /** various statuses for the row data */
  2782. private enum RowStatus {
  2783. INIT, INVALID_PAGE, INVALID_ROW, VALID, DELETED, NORMAL, OVERFLOW;
  2784. }
  2785. /** the phases the RowState moves through as the data is parsed */
  2786. private enum RowStateStatus {
  2787. INIT, AT_HEADER, AT_FINAL;
  2788. }
  2789. /**
  2790. * Maintains state for writing a new row of data.
  2791. */
  2792. protected static class WriteRowState
  2793. {
  2794. private int _complexAutoNumber = ColumnImpl.INVALID_AUTO_NUMBER;
  2795. public int getComplexAutoNumber() {
  2796. return _complexAutoNumber;
  2797. }
  2798. public void setComplexAutoNumber(int complexAutoNumber) {
  2799. _complexAutoNumber = complexAutoNumber;
  2800. }
  2801. public void resetAutoNumber() {
  2802. _complexAutoNumber = ColumnImpl.INVALID_AUTO_NUMBER;
  2803. }
  2804. }
  2805. /**
  2806. * Maintains the state of reading/updating a row of data.
  2807. * @usage _advanced_class_
  2808. */
  2809. public final class RowState extends WriteRowState
  2810. implements ErrorHandler.Location
  2811. {
  2812. /** Buffer used for reading the header row data pages */
  2813. private final TempPageHolder _headerRowBufferH;
  2814. /** the header rowId */
  2815. private RowIdImpl _headerRowId = RowIdImpl.FIRST_ROW_ID;
  2816. /** the number of rows on the header page */
  2817. private int _rowsOnHeaderPage;
  2818. /** the rowState status */
  2819. private RowStateStatus _status = RowStateStatus.INIT;
  2820. /** the row status */
  2821. private RowStatus _rowStatus = RowStatus.INIT;
  2822. /** buffer used for reading overflow pages */
  2823. private final TempPageHolder _overflowRowBufferH =
  2824. TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
  2825. /** the row buffer which contains the final data (after following any
  2826. overflow pointers) */
  2827. private ByteBuffer _finalRowBuffer;
  2828. /** the rowId which contains the final data (after following any overflow
  2829. pointers) */
  2830. private RowIdImpl _finalRowId = null;
  2831. /** true if the row values array has data */
  2832. private boolean _haveRowValues;
  2833. /** values read from the last row */
  2834. private Object[] _rowValues;
  2835. /** null mask for the last row */
  2836. private NullMask _nullMask;
  2837. /** last modification count seen on the table we track this so that the
  2838. rowState can detect updates to the table and re-read any buffered
  2839. data */
  2840. private int _lastModCount;
  2841. /** optional error handler to use when row errors are encountered */
  2842. private ErrorHandler _errorHandler;
  2843. /** cached variable column offsets for jump-table based rows */
  2844. private short[] _varColOffsets;
  2845. private RowState(TempBufferHolder.Type headerType) {
  2846. _headerRowBufferH = TempPageHolder.newHolder(headerType);
  2847. _rowValues = new Object[TableImpl.this.getColumnCount()];
  2848. _lastModCount = TableImpl.this._modCount;
  2849. }
  2850. @Override
  2851. public TableImpl getTable() {
  2852. return TableImpl.this;
  2853. }
  2854. public ErrorHandler getErrorHandler() {
  2855. return((_errorHandler != null) ? _errorHandler :
  2856. getTable().getErrorHandler());
  2857. }
  2858. public void setErrorHandler(ErrorHandler newErrorHandler) {
  2859. _errorHandler = newErrorHandler;
  2860. }
  2861. public void reset() {
  2862. resetAutoNumber();
  2863. _finalRowId = null;
  2864. _finalRowBuffer = null;
  2865. _rowsOnHeaderPage = 0;
  2866. _status = RowStateStatus.INIT;
  2867. _rowStatus = RowStatus.INIT;
  2868. _varColOffsets = null;
  2869. _nullMask = null;
  2870. if(_haveRowValues) {
  2871. Arrays.fill(_rowValues, null);
  2872. _haveRowValues = false;
  2873. }
  2874. }
  2875. public boolean isUpToDate() {
  2876. return(TableImpl.this._modCount == _lastModCount);
  2877. }
  2878. private void checkForModification() {
  2879. if(!isUpToDate()) {
  2880. reset();
  2881. _headerRowBufferH.invalidate();
  2882. _overflowRowBufferH.invalidate();
  2883. int colCount = TableImpl.this.getColumnCount();
  2884. if(colCount != _rowValues.length) {
  2885. // columns added or removed from table
  2886. _rowValues = new Object[colCount];
  2887. }
  2888. _lastModCount = TableImpl.this._modCount;
  2889. }
  2890. }
  2891. private ByteBuffer getFinalPage()
  2892. throws IOException
  2893. {
  2894. if(_finalRowBuffer == null) {
  2895. // (re)load current page
  2896. _finalRowBuffer = getHeaderPage();
  2897. }
  2898. return _finalRowBuffer;
  2899. }
  2900. public RowIdImpl getFinalRowId() {
  2901. if(_finalRowId == null) {
  2902. _finalRowId = getHeaderRowId();
  2903. }
  2904. return _finalRowId;
  2905. }
  2906. private void setRowStatus(RowStatus rowStatus) {
  2907. _rowStatus = rowStatus;
  2908. }
  2909. public boolean isValid() {
  2910. return(_rowStatus.ordinal() >= RowStatus.VALID.ordinal());
  2911. }
  2912. public boolean isDeleted() {
  2913. return(_rowStatus == RowStatus.DELETED);
  2914. }
  2915. public boolean isOverflow() {
  2916. return(_rowStatus == RowStatus.OVERFLOW);
  2917. }
  2918. public boolean isHeaderPageNumberValid() {
  2919. return(_rowStatus.ordinal() > RowStatus.INVALID_PAGE.ordinal());
  2920. }
  2921. public boolean isHeaderRowNumberValid() {
  2922. return(_rowStatus.ordinal() > RowStatus.INVALID_ROW.ordinal());
  2923. }
  2924. private void setStatus(RowStateStatus status) {
  2925. _status = status;
  2926. }
  2927. public boolean isAtHeaderRow() {
  2928. return(_status.ordinal() >= RowStateStatus.AT_HEADER.ordinal());
  2929. }
  2930. public boolean isAtFinalRow() {
  2931. return(_status.ordinal() >= RowStateStatus.AT_FINAL.ordinal());
  2932. }
  2933. private Object setRowCacheValue(int idx, Object value) {
  2934. _haveRowValues = true;
  2935. _rowValues[idx] = value;
  2936. return value;
  2937. }
  2938. private Object getRowCacheValue(int idx) {
  2939. Object value = _rowValues[idx];
  2940. // only return immutable values. mutable values could have been
  2941. // modified externally and therefore could return an incorrect value
  2942. return(ColumnImpl.isImmutableValue(value) ? value : null);
  2943. }
  2944. public Object[] getRowCacheValues() {
  2945. return dupeRow(_rowValues, _rowValues.length);
  2946. }
  2947. public NullMask getNullMask(ByteBuffer rowBuffer) throws IOException {
  2948. if(_nullMask == null) {
  2949. _nullMask = getRowNullMask(rowBuffer);
  2950. }
  2951. return _nullMask;
  2952. }
  2953. private short[] getVarColOffsets() {
  2954. return _varColOffsets;
  2955. }
  2956. private void setVarColOffsets(short[] varColOffsets) {
  2957. _varColOffsets = varColOffsets;
  2958. }
  2959. public RowIdImpl getHeaderRowId() {
  2960. return _headerRowId;
  2961. }
  2962. public int getRowsOnHeaderPage() {
  2963. return _rowsOnHeaderPage;
  2964. }
  2965. private ByteBuffer getHeaderPage()
  2966. throws IOException
  2967. {
  2968. checkForModification();
  2969. return _headerRowBufferH.getPage(getPageChannel());
  2970. }
  2971. private ByteBuffer setHeaderRow(RowIdImpl rowId)
  2972. throws IOException
  2973. {
  2974. checkForModification();
  2975. // don't do any work if we are already positioned correctly
  2976. if(isAtHeaderRow() && (getHeaderRowId().equals(rowId))) {
  2977. return(isValid() ? getHeaderPage() : null);
  2978. }
  2979. // rejigger everything
  2980. reset();
  2981. _headerRowId = rowId;
  2982. _finalRowId = rowId;
  2983. int pageNumber = rowId.getPageNumber();
  2984. int rowNumber = rowId.getRowNumber();
  2985. if((pageNumber < 0) || !_ownedPages.containsPageNumber(pageNumber)) {
  2986. setRowStatus(RowStatus.INVALID_PAGE);
  2987. return null;
  2988. }
  2989. _finalRowBuffer = _headerRowBufferH.setPage(getPageChannel(),
  2990. pageNumber);
  2991. _rowsOnHeaderPage = getRowsOnDataPage(_finalRowBuffer, getFormat());
  2992. if((rowNumber < 0) || (rowNumber >= _rowsOnHeaderPage)) {
  2993. setRowStatus(RowStatus.INVALID_ROW);
  2994. return null;
  2995. }
  2996. setRowStatus(RowStatus.VALID);
  2997. return _finalRowBuffer;
  2998. }
  2999. private ByteBuffer setOverflowRow(RowIdImpl rowId)
  3000. throws IOException
  3001. {
  3002. // this should never see modifications because it only happens within
  3003. // the positionAtRowData method
  3004. if(!isUpToDate()) {
  3005. throw new IllegalStateException(getTable().withErrorContext(
  3006. "Table modified while searching?"));
  3007. }
  3008. if(_rowStatus != RowStatus.OVERFLOW) {
  3009. throw new IllegalStateException(getTable().withErrorContext(
  3010. "Row is not an overflow row?"));
  3011. }
  3012. _finalRowId = rowId;
  3013. _finalRowBuffer = _overflowRowBufferH.setPage(getPageChannel(),
  3014. rowId.getPageNumber());
  3015. return _finalRowBuffer;
  3016. }
  3017. private Object handleRowError(ColumnImpl column, byte[] columnData,
  3018. Exception error)
  3019. throws IOException
  3020. {
  3021. return getErrorHandler().handleRowError(column, columnData,
  3022. this, error);
  3023. }
  3024. @Override
  3025. public String toString() {
  3026. return CustomToStringStyle.valueBuilder(this)
  3027. .append("headerRowId", _headerRowId)
  3028. .append("finalRowId", _finalRowId)
  3029. .toString();
  3030. }
  3031. }
  3032. /**
  3033. * Utility for managing calculated columns. Calculated columns need to be
  3034. * evaluated in dependency order.
  3035. */
  3036. private class CalcColEvaluator
  3037. {
  3038. /** List of calculated columns in this table, ordered by calculation
  3039. dependency */
  3040. private final List<ColumnImpl> _calcColumns = new ArrayList<ColumnImpl>(1);
  3041. private boolean _sorted;
  3042. public void add(ColumnImpl col) {
  3043. if(!getDatabase().isEvaluateExpressions()) {
  3044. return;
  3045. }
  3046. _calcColumns.add(col);
  3047. // whenever we add new columns, we need to re-sort
  3048. _sorted = false;
  3049. }
  3050. public void reSort() {
  3051. // mark columns for re-sort on next use
  3052. _sorted = false;
  3053. }
  3054. public void calculate(Object[] row) throws IOException {
  3055. if(!_sorted) {
  3056. sortColumnsByDeps();
  3057. _sorted = true;
  3058. }
  3059. for(ColumnImpl col : _calcColumns) {
  3060. Object rowValue = col.getCalculationContext().eval(row);
  3061. col.setRowValue(row, rowValue);
  3062. }
  3063. }
  3064. private void sortColumnsByDeps() {
  3065. // a topological sort sorts nodes where A -> B such that A ends up in
  3066. // the list before B (assuming that we are working with a DAG). In our
  3067. // case, we return "descendent" info as Field1 -> Field2 (where Field1
  3068. // uses Field2 in its calculation). This means that in order to
  3069. // correctly calculate Field1, we need to calculate Field2 first, and
  3070. // hence essentially need the reverse topo sort (a list where Field2
  3071. // comes before Field1).
  3072. (new TopoSorter<ColumnImpl>(_calcColumns, TopoSorter.REVERSE) {
  3073. @Override
  3074. protected void getDescendents(ColumnImpl from,
  3075. List<ColumnImpl> descendents) {
  3076. Set<Identifier> identifiers = new LinkedHashSet<Identifier>();
  3077. from.getCalculationContext().collectIdentifiers(identifiers);
  3078. for(Identifier identifier : identifiers) {
  3079. if(isThisTable(identifier)) {
  3080. String colName = identifier.getObjectName();
  3081. for(ColumnImpl calcCol : _calcColumns) {
  3082. // we only care if the identifier is another calc field
  3083. if(calcCol.getName().equalsIgnoreCase(colName)) {
  3084. descendents.add(calcCol);
  3085. }
  3086. }
  3087. }
  3088. }
  3089. }
  3090. }).sort();
  3091. }
  3092. }
  3093. }