From 5c1b068826d0e897b0ea3c4b77c2b8c08503f112 Mon Sep 17 00:00:00 2001 From: James Ahlborn Date: Tue, 11 Mar 2008 00:49:42 +0000 Subject: [PATCH] add unit tests (and fix some bugs) for ignoreNull and unique index handling git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@264 f203690c-595d-4dc9-a70b-905162fa7fd2 --- .../healthmarketscience/jackcess/Column.java | 2 +- .../healthmarketscience/jackcess/Index.java | 106 +++++++-------- .../healthmarketscience/jackcess/Table.java | 16 ++- .../jackcess/UsageMap.java | 3 +- test/data/testIndexProperties.mdb | Bin 0 -> 249856 bytes .../jackcess/CursorTest.java | 7 +- .../jackcess/IndexCodesTest.java | 3 +- .../jackcess/IndexTest.java | 125 ++++++++++++++++-- 8 files changed, 182 insertions(+), 80 deletions(-) create mode 100644 test/data/testIndexProperties.mdb diff --git a/src/java/com/healthmarketscience/jackcess/Column.java b/src/java/com/healthmarketscience/jackcess/Column.java index 9d3583a..6f2ac35 100644 --- a/src/java/com/healthmarketscience/jackcess/Column.java +++ b/src/java/com/healthmarketscience/jackcess/Column.java @@ -1112,7 +1112,7 @@ public class Column implements Comparable { @Override public String toString() { StringBuilder rtn = new StringBuilder(); - rtn.append("\tName: " + _name); + rtn.append("\tName: (" + _table.getName() + ") " + _name); rtn.append("\n\tType: 0x" + Integer.toHexString(_type.getValue()) + " (" + _type + ")"); rtn.append("\n\tNumber: " + _columnNumber); diff --git a/src/java/com/healthmarketscience/jackcess/Index.java b/src/java/com/healthmarketscience/jackcess/Index.java index c87f766..0146c2b 100644 --- a/src/java/com/healthmarketscience/jackcess/Index.java +++ b/src/java/com/healthmarketscience/jackcess/Index.java @@ -498,10 +498,9 @@ public class Index implements Comparable { } if(isLeaf) { - entries.add(new Entry(curEntryBuffer, curEntryLen, _columns)); + entries.add(new Entry(curEntryBuffer, curEntryLen)); } else { - nodeEntries.add(new NodeEntry( - curEntryBuffer, curEntryLen, _columns)); + nodeEntries.add(new NodeEntry(curEntryBuffer, curEntryLen)); } // read any shared "compressed" bytes @@ -558,16 +557,22 @@ public class Index implements Comparable { public void addRow(Object[] row, RowId rowId) throws IOException { - if(shouldIgnoreNulls() && isNullEntry(row)) { + int nullCount = countNullValues(row); + boolean isNullEntry = (nullCount == _columns.size()); + if(shouldIgnoreNulls() && isNullEntry) { // nothing to do return; } + if(isPrimaryKey() && (nullCount > 0)) { + throw new IOException("Null value found in row " + Arrays.asList(row) + + " for primary key index " + this); + } // make sure we've parsed the entries initialize(); - Entry newEntry = new Entry(row, rowId, this); - if(addEntry(newEntry)) { + Entry newEntry = new Entry(createEntryBytes(row), rowId); + if(addEntry(newEntry, isNullEntry, row)) { ++_rowCount; ++_modCount; } else { @@ -587,7 +592,8 @@ public class Index implements Comparable { public void deleteRow(Object[] row, RowId rowId) throws IOException { - if(shouldIgnoreNulls() && isNullEntry(row)) { + int nullCount = countNullValues(row); + if(shouldIgnoreNulls() && (nullCount == _columns.size())) { // nothing to do return; } @@ -595,7 +601,7 @@ public class Index implements Comparable { // make sure we've parsed the entries initialize(); - Entry oldEntry = new Entry(row, rowId, this); + Entry oldEntry = new Entry(createEntryBytes(row), rowId); if(removeEntry(oldEntry)) { --_rowCount; ++_modCount; @@ -638,18 +644,16 @@ public class Index implements Comparable { initialize(); Position startPos = FIRST_POSITION; if(startRow != null) { - Entry startEntry = new Entry(startRow, + Entry startEntry = new Entry(createEntryBytes(startRow), (startInclusive ? - RowId.FIRST_ROW_ID : RowId.LAST_ROW_ID), - this); + RowId.FIRST_ROW_ID : RowId.LAST_ROW_ID)); startPos = new Position(FIRST_ENTRY_IDX, startEntry); } Position endPos = LAST_POSITION; if(endRow != null) { - Entry endEntry = new Entry(endRow, + Entry endEntry = new Entry(createEntryBytes(endRow), (endInclusive ? - RowId.LAST_ROW_ID : RowId.FIRST_ROW_ID), - this); + RowId.LAST_ROW_ID : RowId.FIRST_ROW_ID)); endPos = new Position(LAST_ENTRY_IDX, endEntry); } return new EntryCursor(startPos, endPos); @@ -674,7 +678,7 @@ public class Index implements Comparable { /** * Adds an entry to the _entries list, maintaining the order. */ - private boolean addEntry(Entry newEntry) + private boolean addEntry(Entry newEntry, boolean isNullEntry, Object[] row) throws IOException { int idx = findEntry(newEntry); @@ -683,16 +687,17 @@ public class Index implements Comparable { idx = missingIndexToInsertionPoint(idx); // determine if the addition of this entry would break the uniqueness - // constraint - if(isUnique() && + // constraint (note, access does not seem to consider multiple null + // entries invalid for a "unique" index) + if(isUnique() && !isNullEntry && (((idx > 0) && newEntry.equalsEntryBytes(_entries.get(idx - 1))) || ((idx < _entries.size()) && newEntry.equalsEntryBytes(_entries.get(idx))))) { throw new IOException( - "New entry " + newEntry + - " violates uniqueness constrain for index " + this); + "New row " + Arrays.asList(row) + + " violates uniqueness constraint for index " + this); } _entries.add(idx, newEntry); @@ -787,7 +792,7 @@ public class Index implements Comparable { @Override public String toString() { StringBuilder rtn = new StringBuilder(); - rtn.append("\tName: " + _name); + rtn.append("\tName: (" + _table.getName() + ") " + _name); rtn.append("\n\tNumber: " + _indexNumber); rtn.append("\n\tPage number: " + _pageNumber); rtn.append("\n\tIs Primary Key: " + isPrimaryKey()); @@ -809,27 +814,29 @@ public class Index implements Comparable { } /** - * Determines if all values for this index from the given row are - * {@code null}. + * Determines the number of {@code null} values for this index from the + * given row. */ - private boolean isNullEntry(Object[] values) + private int countNullValues(Object[] values) { if(values == null) { - return true; + return _columns.size(); } // annoyingly, the values array could come from different sources, one // of which will make it a different size than the other. we need to // handle both situations. boolean useColNumber = (values.length >= _table.getMaxColumnCount()); + int nullCount = 0; for(ColumnDescriptor col : _columns) { Object value = values[ useColNumber ? col.getColumnNumber() : col.getColumnIndex()]; - if(!col.isNullValue(value)) { - return false; + if(col.isNullValue(value)) { + ++nullCount; } } - return true; + + return nullCount; } /** @@ -1039,7 +1046,7 @@ public class Index implements Comparable { */ private static Entry createSpecialEntry(RowId rowId) { try { - return new Entry(null, rowId, null); + return new Entry((byte[])null, rowId); } catch(IOException e) { // should never happen throw new IllegalStateException(e); @@ -1369,47 +1376,41 @@ public class Index implements Comparable { /** * Create a new entry - * @param values Indexed row values + * @param entryBytes encoded bytes for this index entry * @param rowId rowId in which the row is stored - * @param columns map of columns for this index */ - private Entry(Object[] values, RowId rowId, Index parent) + private Entry(byte[] entryBytes, RowId rowId) throws IOException { _rowId = rowId; - if(values != null) { - _entryBytes = parent.createEntryBytes(values); + _entryBytes = entryBytes; + if(_entryBytes != null) { _type = ((_rowId.getType() == RowId.Type.NORMAL) ? EntryType.NORMAL : ((_rowId.getType() == RowId.Type.ALWAYS_FIRST) ? EntryType.FIRST_VALID : EntryType.LAST_VALID)); + } else if(!_rowId.isValid()) { + // this is a "special" entry (first/last) + _type = ((_rowId.getType() == RowId.Type.ALWAYS_FIRST) ? + EntryType.ALWAYS_FIRST : EntryType.ALWAYS_LAST); } else { - if(!_rowId.isValid()) { - // this is a "special" entry (first/last) - _entryBytes = null; - _type = ((_rowId.getType() == RowId.Type.ALWAYS_FIRST) ? - EntryType.ALWAYS_FIRST : EntryType.ALWAYS_LAST); - } else { - throw new IllegalArgumentException("Values was null"); - } + throw new IllegalArgumentException("Values was null for valid entry"); } } /** * Read an existing entry in from a buffer */ - private Entry(ByteBuffer buffer, int entryLen, - List columns) + private Entry(ByteBuffer buffer, int entryLen) throws IOException { - this(buffer, entryLen, columns, 0); + this(buffer, entryLen, 0); } /** * Read an existing entry in from a buffer */ - private Entry(ByteBuffer buffer, int entryLen, - List columns, int extraTrailingLen) + private Entry(ByteBuffer buffer, int entryLen, int extraTrailingLen) throws IOException { // we need 4 trailing bytes for the rowId, plus whatever the caller @@ -1439,7 +1440,7 @@ public class Index implements Comparable { public boolean isValid() { return(_entryBytes != null); } - + protected final byte[] getEntryBytes() { return _entryBytes; } @@ -1534,12 +1535,11 @@ public class Index implements Comparable { /** * Read an existing node entry in from a buffer */ - private NodeEntry(ByteBuffer buffer, int entryLen, - List columns) + private NodeEntry(ByteBuffer buffer, int entryLen) throws IOException { // we need 4 trailing bytes for the sub-page number - super(buffer, entryLen, columns, 4); + super(buffer, entryLen, 4); _subPageNumber = ByteUtil.getInt(buffer, ByteOrder.BIG_ENDIAN); } @@ -1665,7 +1665,8 @@ public class Index implements Comparable { public void beforeEntry(Object[] row) throws IOException { - restorePosition(new Entry(row, RowId.FIRST_ROW_ID, Index.this)); + restorePosition( + new Entry(Index.this.createEntryBytes(row), RowId.FIRST_ROW_ID)); } /** @@ -1675,7 +1676,8 @@ public class Index implements Comparable { public void afterEntry(Object[] row) throws IOException { - restorePosition(new Entry(row, RowId.LAST_ROW_ID, Index.this)); + restorePosition( + new Entry(Index.this.createEntryBytes(row), RowId.LAST_ROW_ID)); } /** diff --git a/src/java/com/healthmarketscience/jackcess/Table.java b/src/java/com/healthmarketscience/jackcess/Table.java index eeea925..311f0ad 100644 --- a/src/java/com/healthmarketscience/jackcess/Table.java +++ b/src/java/com/healthmarketscience/jackcess/Table.java @@ -1094,14 +1094,17 @@ public class Table } getPageChannel().readPage(dataPage, tmpPageNumber); if(dataPage.get() == PageTypes.DATA) { - // found last data page - pageNumber = tmpPageNumber; + // found last data page, only use if actually listed in free space + // pages + if(_freeSpacePages.containsPageNumber(tmpPageNumber)) { + pageNumber = tmpPageNumber; + } break; } } if(pageNumber == PageChannel.INVALID_PAGE_NUMBER) { - //No data pages exist. Create a new one. + // No data pages exist (with free space). Create a new one. pageNumber = newDataPage(dataPage); } @@ -1111,12 +1114,13 @@ public class Table short freeSpaceInPage = dataPage.getShort(getFormat().OFFSET_FREE_SPACE); if (freeSpaceInPage < rowSpaceUsage) { - //Last data page is full. Create a new one. + // Last data page is full. Create a new one. writeDataPage(dataPage, pageNumber); - dataPage.clear(); _freeSpacePages.removePageNumber(pageNumber); + dataPage.clear(); pageNumber = newDataPage(dataPage); + freeSpaceInPage = dataPage.getShort(getFormat().OFFSET_FREE_SPACE); } @@ -1131,7 +1135,7 @@ public class Table } writeDataPage(dataPage, pageNumber); - //Update tdef page + // Update tdef page updateTableDefinition(rows.size()); } diff --git a/src/java/com/healthmarketscience/jackcess/UsageMap.java b/src/java/com/healthmarketscience/jackcess/UsageMap.java index 551c8a5..9484ffb 100644 --- a/src/java/com/healthmarketscience/jackcess/UsageMap.java +++ b/src/java/com/healthmarketscience/jackcess/UsageMap.java @@ -384,7 +384,8 @@ public class UsageMap @Override public String toString() { - StringBuilder builder = new StringBuilder("page numbers: ["); + StringBuilder builder = new StringBuilder( + "page numbers (range " + _startPage + " " + _endPage + "): ["); PageCursor pCursor = cursor(); while(true) { int nextPage = pCursor.getNextPage(); diff --git a/test/data/testIndexProperties.mdb b/test/data/testIndexProperties.mdb new file mode 100644 index 0000000000000000000000000000000000000000..bb1c1581e0ba3626fa2063097985487dc1bd36dd GIT binary patch literal 249856 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

c=FE2j&{>BRJtU@1rL~3MWfM6>6M|pt4oh z1N6n6pRBKPvgoUv##vvLtwmqlg30Uv^vbE@oBA}uka28POB^NX#<>V8+jtQ)?Fl#a3u(&~uX_%7p77k`xxo|hEcIOI$@9GB{yELe;*AL~0Vco% zm;e)C0!)AjFaajO1em}{NdRLZ*kb)%ksU?8Y^*Fx#~K&B>3)~iIgsnTSbwxT)ZXq5 zN4kPuI7H8Z4KfPlu*5CPG9A-w;+nsjQ7?)0H+4k1w_t%N;!RFO>z!$4IXBkd9_fyH zBQ4%&_qLXnvlC(BYL%*_&G+w8s}g7x2?ui%;eVtQ#)y-lA5@7a;*a%{{wlo9hIRg` z*p~2eVI7s)N2P{B6IBLSC+wiVcNX_`&C{rMl869%@ zu=1ZJ*1&~m)zN?-(~|Utr$q*4BJ#`{CQ3TxnWF{SA$cw`KvJ2o%Mco_LO5W>FIGK6 zcojpBx_C*ro^!9Ea2f*OhEYiGWxT!N!NW`L+_?AI6_-2u@w#kK>U1^9|3U)4 zzdXEZ^1J=TiywaUmgj$U-?j%*g%2QSm|!BMQf#!0*QlSZo+v6TE@;NudJT0i!7wt0 zY@_!!RLzr|2AMXZL&h2)XqrO_ut+FiT0*mB;6W88Kb>asNFfsf+-WH>0<|LzV^ysa zC&p0uc;&@ai*fOftZF&Z&Emuw+BLYWt6|e_EzTTSo-)p$aWk!w7kO|9-_^JcdT@be znYN**g6l#hn5cTGYB#F!sAb){kv7GmTPZBBm|SSlfohd3BT@Aq6>UhV3Q{Lrj2M#! zJrGFaZOAnjHkT-!E`+7qwH5I%!nHbdMQ}(~1h2;{f^WZhr}rl3&bqI+U;NU2z~n9 zAzVRl1(9emas`1~%Cd08uE_5eQFG=~g!HrTNgEAq=X3qo~ zlDUmO7`wHaxLAu2Wbns$5WCQ4qdrqR8Vj>6`oX9P^mCz%Wk{tIM_g|(0H z=>>%JpjN*o5+3_{0`c~6;!$%wJ!%CEK<|phY_V8REVeI6)}aB}|0O+droe*YAnl}Z z*7!EWA$!3ThaRy;u}MFvBQ?Xx5yok0OB4e?5`HphE>5k{uBKKJbzXZg#geYLklF6p zDVD-@CnCzk_=^cJ0Vco%m;e)C0!)AjFaajO1em~yBA{tMmHmIp-!8t1_5VelLvfcA zr4{GP1egF5U;<2l2`~XBzyz286JP>NVDtpW#HzjVet7iCj7d7jo`_w0ESn51rP`e3 zLeJ?FcCXMN@^&f`=nVy;b7{JQUyxWPMUBMyQq)PjOo}!V7fR7iV!0I4NUW4%I*HX% zbdXppMJI`irHDDpyfFbLzyz286JP>NfC(@GCcp%kz{yF#u01b-4&O@?UiZ40m8 zxY0p=6ur&p?7IiiF9enzhM}l6@v?zj8!p0%<)!#<1n=H9Txmy@bchzc)SP|3$In@%x^B%?{f}0hmIl zH*GAD#iBe|NotN;i&!MhDFl*Sl{vf|)3!vXG{Vr0RsJqZN^#o-FDu&vzmLTR$TrZM z2K|5EV|#gk?E=^d8!C<+an3d4Tqny;`AEB#L&ix%9PfZQiPHh+B%1~X_@QkfknIQB zE%E3VPA1Wum6D%fgCoIdU?{~iIaHd^&FhP;CrHHn#RQlD6JP>NfC(@GCcp%k023G; z0Zm&RU+teBU;qDryE*PUy7W0V6JP>NfC(@GCcp%k025#WOn?cTaRlgf?r68Z(I!Cg z%f}*-(U;<2l2`~XBzyz286JP>NfC(@GCcp$z6X5=TYA)=`1egF5 zU;<2l2`~XBzyz286JP>N;B+Fu{r}VHY;swc025#WOn?b60Vco%m;e)C0!)Ajq$Z$i zx5}0NtyrC5H4I0KHFoXmv8jC-S=}=%?@9Y_GhibQ`%s%S)Mt1~dwg>3$F6+=!%!cY z_R|b|e9~iIPXJ~CyF^6n5Kh>UYZBeq$+jKB#bUdZJ-mHMvJMSqA{WwgugooOcW=lz zHpH1_#xd;fS&N-)*KP=Vcv9O5@Gms!6x8Rxb8%v1`7UW z0!)AjFaajO1egF5U;<2l2`~XBaDoVE+E3$_{_Ju4e$8zu(GVt~y|$R0Ng z;EEdtC=vRhKN5a2=&_gyNM$%6DPQ?>me_+&H;fE~MJJ?C+z`PYNKJ4J3ZFO@ z4P;69iwQ6RCcp%k025#WOn?b60Vco%P8$N6_T%{a|1`P&fBS(V&!M>6X;V-x2oqoe zOn?b60Vco%m;e)C0!)AjoLU5o7p|k!c}x(zLe*gbLya>lmn{}+fWjGnH`3*T?|9|NB{{>_5{vR*p+vV&09rC+Neh2YwgQ9bx zOf}w*4}Sk|gmcT#HS#-vZ#&}5N1R5yLBCEGgx~)g=aavg025#WOn?b60Vco%m;e)C z0!-jkBw*L>&pvMm#hvBC?iCsidQP7hlQEMaa$@J>MW8nnh|Z;vsv<#RnG`iC8qS#{ z&zH_RiI+(+i^PRew3Apa#WWHtrI=1)wGNfC(@GCU6V{H0>wp^=m8YDXK?^gQ-6=GH3iya~wnRr*M>pRen*4i4}dKKv-u& zG+{PGNJKCzB3HTxFhihAREnMW)q%+n%glB64LEipZZ-T{Aa4`xh`APVgJK8VtB^)J zT*HX94z2de2w%c4G6zjh9{p6kt0^&cr{WAA#@Por-=%r+>Z2jh%4aI1*u+! zZIyXUl`|otqLYevzAWNYDAt51q(-8EtEAFFk=l1PlI%f(L*dC1wMdT&O)9vPV2uWz zQ!El|#1a%^>@XT9Dk?TZ*APyeI>}Uu2;9PwOD=NlfbTZ=(+Q4>Hk7OiQX{*dy{XnP&c`Nh=hJzBl7r`OgBi4wP=Q8J_5#u0k9=c81 zk)yHw{ZPi+8y-Bo z8&`Of86){X7EF}!k%S+Lsyk3reUi{JoH&umN^C_~O;F@?d2)7;Y|Fr?DVYNy*##LT zm((&z0!M9v5^^DEE}_{b!U#%OGR`yo^n3_NO4AA`P`k{DCS5tro3$xrUI;MiytocT zH{*qdY{B`St) zIHhiYj;v}Pz8Pw5N>WRGEE<5hm^DnLvo@eM)1%+gfTGrdd_0paG%-sPnYx&&sRqqW4kQ#W#_J6;$u;A5KB8zU!zw5@D zyGy50<0v^NYh!BLqd)lTg@5m@y!%Gi^t&HDygyZV)tD+W^LX*4Y1iHGuhZ+k`1SL8 z{(I7wZyhd<-;9%dpjK4sCYTMdWD{&6nqbMQr*4ATElsdA$h1+zHQhJ?8oIS+Tg(YT zHo-FDO)xabWIIi#2AgbxWl^9Gq@h6rVKGEkBpYGJBexHy$jv{Z-1Kay8ABnm+$fTd zT9rm|siA3<(ysLl`TpOYQ;zt3lzk4B77}pqc1jWG?T!8EalHP|6p&&+f|MnC`(zP{ zoKnKATcq{YH3!1$R)!pSfv-`}zg`1pNAJc!xXlr0bhL#djg9c=6El0^k>0y05Lzj( zh9bSUEwBl)O4Z)m7GA$`qk}?Fa&0ob*t>|90WXyf*^N}S#wkUVm1P1&Y0^+E3&5`|Q&GKU@DlVJCw#W&%ur2`~XBzyz286JP>NfC(^xF(yzx7U_A% zB9RlFhF$ZzFFMgmaQ;ky2`~XBzyz286JP>NfC(@GCcp%kz;PwO{r}^NmlJ0KOn?b6 z0Vco%m;e)C0!)AjFaaiTQV`IzAIInar^)&M+Yj&xfRp0Xuo6sw2`~XBzyz286JP>N zfC(^xGo65aEC7r}BITo1WYh_e0;5Yr(_YZj|Nqm(uLqd@qttmjl?m+swmtrzG{`Jr z@X}&nss#5T;>0`%Hsr zSP58VzBW?PmN zfC(@GCcp%k025#W0|?l}k7(Sqzbmq%SY+Xoj6kJYa*;A=({R` zd9nUzcc{JH8;*1Zy+|>7j+vmt7|E8|lv$!)^H(#MOJe;^9g*%W!7hq7IT5XQCjK)V zX*W04-yZ3XdLu2~X!o|3ma`LKs6aAIsY+V2_eB7DkH#1rwy`bmEk-e!~k zQeCEx|MMOPL?h7B(MP3*LK9U6n)a@H5+#StCH~;~ndfQGBc9tmAN90)mUzlM1s=nX zznK6NU;<2l2`~XBzyz286JP>NfC-#_1azafJa~*?vT$O^K^HbNM48ic!2h3@9qfT) z1Nlk8lg9HcPB>bdPuBbLOT_QT5bx`z(Jh1}h2w}2(6OKE@Qn>D<%*}1E z%b(23?SK;f1e5js6#G0 z`lTKcW&gi4heX-uFP%W5?DLoAk|_K9r4vb%eg4us5@nyiG@nG-=PxZFQTF*u&mvLw z`Ad}{$x{Ig>&OI{025#WOn?b60Vco%m;e(v!w6_vG`)UpMLiWeUxqZFbI2p-C`(rvXmHt-NX*q0;KR{g|JIh!sg&|xVgnL z(^g;uj-5!e8vZSiw~2PdT#L9t*jy|SRY;>9u3^Ml2iE|6wk*r;vz3yLRZcQ>IKMrf}^j!4lCR zYOqIS*`}Gl8)XV)AF)Gh#<%jHCDy|kUhvwgfjA`SP{69g~)E4HL@MqiTrSm z;Sv>XNT~`^BF3aaXN@%8hFo(+qfDm@u~#EbE86~=wHY}CQI1xWgi02~RY%uXJ96|>LfB$|b6xuXCb;VMp7+}YFMsfj zMl$W0oW5cIQwA69554#F%GaM<_Soi6G`_R#$@fx)S9U^+j3{DzWWvqU&wi!iwrlO} zf17gn_2J?uz09cbLl&gL|HvGObQ=)qlZ2L$jf*^4^}UFu;esHzMlm4U?3gn}HbY5H zgG}^^b~;JmuQ`<9grK>EX3Kyj(y%-?_+^}=ObBq-v@8mwG)M+|qtt**vAWDlxe5OY zCY2TlGG9W@ca~YW8%K$I*@8%t^=pjoi=1W z_o|?J?j@n>xx5#yc;iv=;*vWbnf$qLd@OA{#a(_~Ra5GE&Un7i6D}yOLcJTr{n|-U zuoYhYySwf>ud@CVH!Yu*yW{)WR0AcaZms2B4B!4gm9IWj_vuT&di|3Ze4!v!cvZ{E z3KwY_ve4=dm?3{%39g@2URz>e*N6-bFck*`OULmdaJ1T+y6$@M{@es z>gLJ|Cj9E!jm7n!$;tao&P@m3P8D8NH>y$PUi+ON{4MRjYVUmWhYK&OeS80KaYosw zN>+XRE2(6lC#F%Ae437Hqsp#*d8qgQ-Wzg6IV|COQBh+h09~oPoq_~PsaH^%OJ8~P zbyja(b0EBKWypaS_ta9t+x(4zaGN90=x7T^8XKise($D0Xr&Y<^tJ^y8REp=w($Cm z8y)0F(c5HzNpMy1`2R(MC~r)F2`~XBzyz286JP>NfC(^xlaGL1+cVVq|MHx2p`qKa zV+^32Mgk!5D8R{g3XiB!K^g@qaEadDSnNlTY@)YM#wu`134?tF+Q3l(1pVtZaCRgQ z4It*sUU;O(#|K1#ZVnKrRPBRD2`KA#5iLUv6;N1Mk1UXS@D~$c0!)AjFaajO1egF5 zU;<2l2^?<%n)a$o&Hu;5;Q0Lif4Gi!&YV6IU;<2l37iN5JolC7zK-|Y*W^_*blvlU zB3@8rS=Kb}3yM-Lw&Vpxyr{AX>-cz4rQk)C8kT#W#)~R5w0AtJ|Bt!c2Tw#>l=D1Z z1onU19$y|vE0L2o%6BA7xD(49w_{;BnG86cHd;U_r%0Smg*xW43|>xp%t@UF5t)Yd zBr*-AVI^Rd`PxWDV;UUkpCtFAan(YL<;;zq#Po9`c1*C8+YihmxvT%lY&!9Q2`~XB Lzyz4Um=gH^i|@8s literal 0 HcmV?d00001 diff --git a/test/src/java/com/healthmarketscience/jackcess/CursorTest.java b/test/src/java/com/healthmarketscience/jackcess/CursorTest.java index 0aa9c70..531805a 100644 --- a/test/src/java/com/healthmarketscience/jackcess/CursorTest.java +++ b/test/src/java/com/healthmarketscience/jackcess/CursorTest.java @@ -105,12 +105,7 @@ public class CursorTest extends TestCase { } static Database createTestIndexTable() throws Exception { - File srcFile = new File("test/data/indexCursorTest.mdb"); - File dbFile = File.createTempFile("databaseTest", ".mdb"); - dbFile.deleteOnExit(); - copyFile(srcFile, dbFile); - - Database db = Database.open(dbFile); + Database db = openCopy(new File("test/data/indexCursorTest.mdb")); Table table = db.getTable("test"); diff --git a/test/src/java/com/healthmarketscience/jackcess/IndexCodesTest.java b/test/src/java/com/healthmarketscience/jackcess/IndexCodesTest.java index 8e3ee2b..c057656 100644 --- a/test/src/java/com/healthmarketscience/jackcess/IndexCodesTest.java +++ b/test/src/java/com/healthmarketscience/jackcess/IndexCodesTest.java @@ -31,7 +31,6 @@ import java.io.File; import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -376,7 +375,7 @@ public class IndexCodesTest extends TestCase { return builder.toString(); } - private static String entryToString(Cursor.Position curPos) + static String entryToString(Cursor.Position curPos) throws Exception { Field eField = curPos.getClass().getDeclaredField("_entry"); diff --git a/test/src/java/com/healthmarketscience/jackcess/IndexTest.java b/test/src/java/com/healthmarketscience/jackcess/IndexTest.java index 917fc81..bb3b7f1 100644 --- a/test/src/java/com/healthmarketscience/jackcess/IndexTest.java +++ b/test/src/java/com/healthmarketscience/jackcess/IndexTest.java @@ -28,6 +28,7 @@ King of Prussia, PA 19406 package com.healthmarketscience.jackcess; import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -149,12 +150,7 @@ public class IndexTest extends TestCase { db.close(); // copy to temp file and attemp to edit - File testFile = File.createTempFile("databaseTest", ".mdb"); - testFile.deleteOnExit(); - - copyFile(origFile, testFile); - - db = Database.open(testFile); + db = openCopy(origFile); t = db.getTable("Table1"); try { @@ -167,12 +163,7 @@ public class IndexTest extends TestCase { } public void testEntryDeletion() throws Exception { - File srcFile = new File("test/data/test.mdb"); - File dbFile = File.createTempFile("databaseTest", ".mdb"); - dbFile.deleteOnExit(); - copyFile(srcFile, dbFile); - - Table table = Database.open(dbFile).getTable("Table1"); + Table table = openCopy(new File("test/data/test.mdb")).getTable("Table1"); for(int i = 0; i < 10; ++i) { table.addRow("foo" + i, "bar" + i, (byte)42 + i, (short)53 + i, 13 * i, @@ -207,6 +198,116 @@ public class IndexTest extends TestCase { } } + public void testIgnoreNulls() throws Exception + { + Database db = openCopy(new File("test/data/testIndexProperties.mdb")); + + doTestIgnoreNulls(db, "TableIgnoreNulls1"); + doTestIgnoreNulls(db, "TableIgnoreNulls2"); + + db.close(); + } + + private void doTestIgnoreNulls(Database db, String tableName) + throws Exception + { + Table orig = db.getTable(tableName); + Index origI = orig.getIndex("DataIndex"); + Table temp = db.getTable(tableName + "_temp"); + Index tempI = temp.getIndex("DataIndex"); + + // copy from orig table to temp table + for(Map row : orig) { + temp.addRow(orig.asRow(row)); + } + + assertEquals(origI.getEntryCount(), tempI.getEntryCount()); + + Cursor origC = Cursor.createIndexCursor(orig, origI); + Cursor tempC = Cursor.createIndexCursor(temp, tempI); + + while(true) { + boolean origHasNext = origC.moveToNextRow(); + boolean tempHasNext = tempC.moveToNextRow(); + assertTrue(origHasNext == tempHasNext); + if(!origHasNext) { + break; + } + + Map origRow = origC.getCurrentRow(); + Cursor.Position origCurPos = origC.getSavepoint().getCurrentPosition(); + Map tempRow = tempC.getCurrentRow(); + Cursor.Position tempCurPos = tempC.getSavepoint().getCurrentPosition(); + + assertEquals(origRow, tempRow); + assertEquals(IndexCodesTest.entryToString(origCurPos), + IndexCodesTest.entryToString(tempCurPos)); + } + } + + public void testUnique() throws Exception + { + Database db = openCopy(new File("test/data/testIndexProperties.mdb")); + + Table t = db.getTable("TableUnique1_temp"); + Index index = t.getIndex("DataIndex"); + + doTestUnique(t, index, 1, + null, true, + "unique data", true, + null, true, + "more", false, + "stuff", false, + "unique data", false); + + t = db.getTable("TableUnique2_temp"); + index = t.getIndex("DataIndex"); + + doTestUnique(t, index, 2, + null, null, true, + "unique data", 42, true, + "unique data", null, true, + null, null, true, + "some", 42, true, + "more unique data", 13, true, + null, -4242, true, + "another row", -3462, false, + null, 49, false, + "more", null, false, + "unique data", 42, false, + "unique data", null, false, + null, -4242, false); + + db.close(); + } + + private void doTestUnique(Table t, Index index, int numValues, + Object... testData) + throws Exception + { + for(int i = 0; i < testData.length; i += (numValues + 1)) { + Object[] row = new Object[numValues + 1]; + row[0] = "testRow" + i; + for(int j = 1; j < (numValues + 1); ++j) { + row[j] = testData[i + j - 1]; + } + boolean expectedSuccess = (Boolean)testData[i + numValues]; + + IOException failure = null; + try { + index.addRow(row, new RowId(400 + i, 0)); + } catch(IOException e) { + failure = e; + } + if(expectedSuccess) { + assertNull(failure); + } else { + assertTrue(failure != null); + assertTrue(failure.getMessage().contains("uniqueness")); + } + } + } + private void checkIndexColumns(Table table, String... idxInfo) throws Exception { -- 2.39.5