From fad035e0b93b55502f54474343c9ac618a62e66d Mon Sep 17 00:00:00 2001 From: James Ahlborn Date: Mon, 16 Mar 2015 20:16:51 +0000 Subject: [PATCH] Implement support for indexes on BINARY fields git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@921 f203690c-595d-4dc9-a70b-905162fa7fd2 --- src/changes/changes.xml | 3 + .../jackcess/impl/IndexCodes.java | 4 - .../jackcess/impl/IndexData.java | 132 +++++++++++++----- .../jackcess/util/SimpleColumnMatcher.java | 16 ++- src/test/data/V2010/binIdxTestV2010.accdb | Bin 0 -> 417792 bytes .../jackcess/IndexTest.java | 39 ++++++ .../jackcess/impl/JetFormatTest.java | 3 +- 7 files changed, 158 insertions(+), 39 deletions(-) create mode 100644 src/test/data/V2010/binIdxTestV2010.accdb diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 0c866c0..dcd9f90 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -28,6 +28,9 @@ Added contextual information to many errors and warnings. + + Implement support for indexes on BINARY fields. + diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/IndexCodes.java b/src/main/java/com/healthmarketscience/jackcess/impl/IndexCodes.java index a605883..6fbbc65 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/IndexCodes.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/IndexCodes.java @@ -40,10 +40,6 @@ public class IndexCodes { static final byte DESC_START_FLAG = (byte)0x80; static final byte DESC_NULL_FLAG = (byte)0xFF; - static final byte MID_GUID = (byte)0x09; - static final byte ASC_END_GUID = (byte)0x08; - static final byte DESC_END_GUID = (byte)0xF7; - static final byte ASC_BOOLEAN_TRUE = (byte)0x00; static final byte ASC_BOOLEAN_FALSE = (byte)0xFF; diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java b/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java index a6970a9..f951dc6 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java @@ -1313,6 +1313,71 @@ public class IndexData { return column.write(value, 0, ENTRY_BYTE_ORDER).array(); } + /** + * Writes a binary value using the general binary entry encoding rules. + */ + private static void writeGeneralBinaryEntry(byte[] valueBytes, boolean isAsc, + ByteStream bout) + { + int dataLen = valueBytes.length; + int extraLen = (dataLen + 7) / 8; + int entryLen = ((dataLen + extraLen + 8) / 9) * 9; + + // reserve space for the full entry + bout.ensureNewCapacity(entryLen); + + // binary data is written in 8 byte segments with a trailing length byte. + // The length byte is the amount of valid bytes in the segment (where 9 + // indicates that there is more data _after_ this segment). + byte[] partialEntryBytes = new byte[9]; + + // bit twiddling rules: + // - isAsc => nothing + // - !isAsc => flipBytes, _but keep intermediate 09 unflipped_! + + // first, write any intermediate segements + int segmentLen = dataLen; + int pos = 0; + while(segmentLen > 8) { + + System.arraycopy(valueBytes, pos, partialEntryBytes, 0, 8); + if(!isAsc) { + // note, we do _not_ flip the length byte for intermediate segments + flipBytes(partialEntryBytes, 0, 8); + } + + // we are writing intermediate segments (there is more data after this + // segment), so the length is always 9. + partialEntryBytes[8] = (byte)9; + + pos += 8; + segmentLen -= 8; + + bout.write(partialEntryBytes); + } + + // write the last segment (with slightly different rules) + if(segmentLen > 0) { + + System.arraycopy(valueBytes, pos, partialEntryBytes, 0, segmentLen); + + // clear out an intermediate bytes between the real data and the final + // length byte + for(int i = segmentLen; i < 8; ++i) { + partialEntryBytes[i] = 0; + } + + partialEntryBytes[8] = (byte)segmentLen; + + if(!isAsc) { + // note, we _do_ flip the last length byte + flipBytes(partialEntryBytes, 0, 9); + } + + bout.write(partialEntryBytes); + } + } + /** * Creates one of the special index entries. */ @@ -1359,6 +1424,8 @@ public class IndexData { return new BooleanColumnDescriptor(col, flags); case GUID: return new GuidColumnDescriptor(col, flags); + case BINARY: + return new BinaryColumnDescriptor(col, flags); default: // we can't modify this index at this point in time @@ -1467,15 +1534,14 @@ public class IndexData { writeNonNullValue(value, bout); } - protected abstract void writeNonNullValue( - Object value, ByteStream bout) + protected abstract void writeNonNullValue(Object value, ByteStream bout) throws IOException; @Override public String toString() { return CustomToStringStyle.builder(this) .append("column", getColumn()) - .append("flags", getFlags()) + .append("flags", getFlags() + " " + (isAscending() ? "(ASC)" : "(DSC)")) .toString(); } } @@ -1492,8 +1558,7 @@ public class IndexData { } @Override - protected void writeNonNullValue( - Object value, ByteStream bout) + protected void writeNonNullValue(Object value, ByteStream bout) throws IOException { byte[] valueBytes = encodeNumberColumnValue(value, getColumn()); @@ -1524,8 +1589,7 @@ public class IndexData { } @Override - protected void writeNonNullValue( - Object value, ByteStream bout) + protected void writeNonNullValue(Object value, ByteStream bout) throws IOException { byte[] valueBytes = encodeNumberColumnValue(value, getColumn()); @@ -1575,8 +1639,7 @@ public class IndexData { } @Override - protected void writeNonNullValue( - Object value, ByteStream bout) + protected void writeNonNullValue(Object value, ByteStream bout) throws IOException { byte[] valueBytes = encodeNumberColumnValue(value, getColumn()); @@ -1641,8 +1704,7 @@ public class IndexData { } @Override - protected void writeNonNullValue( - Object value, ByteStream bout) + protected void writeNonNullValue(Object value, ByteStream bout) throws IOException { byte[] valueBytes = encodeNumberColumnValue(value, getColumn()); @@ -1699,8 +1761,7 @@ public class IndexData { } @Override - protected void writeNonNullValue( - Object value, ByteStream bout) + protected void writeNonNullValue(Object value, ByteStream bout) throws IOException { GeneralLegacyIndexCodes.GEN_LEG_INSTANCE.writeNonNullIndexTextValue( @@ -1720,8 +1781,7 @@ public class IndexData { } @Override - protected void writeNonNullValue( - Object value, ByteStream bout) + protected void writeNonNullValue(Object value, ByteStream bout) throws IOException { GeneralIndexCodes.GEN_INSTANCE.writeNonNullIndexTextValue( @@ -1741,29 +1801,37 @@ public class IndexData { } @Override - protected void writeNonNullValue( - Object value, ByteStream bout) + protected void writeNonNullValue(Object value, ByteStream bout) throws IOException { - byte[] valueBytes = encodeNumberColumnValue(value, getColumn()); - - // index format <8-bytes> 0x09 <8-bytes> 0x08 - - // bit twiddling rules: - // - isAsc => nothing - // - !isAsc => flipBytes, _but keep 09 unflipped_! - if(!isAscending()) { - flipBytes(valueBytes); - } - - bout.write(valueBytes, 0, 8); - bout.write(MID_GUID); - bout.write(valueBytes, 8, 8); - bout.write(isAscending() ? ASC_END_GUID : DESC_END_GUID); + writeGeneralBinaryEntry( + encodeNumberColumnValue(value, getColumn()), isAscending(), + bout); } } + /** + * ColumnDescriptor for BINARY columns. + */ + private static final class BinaryColumnDescriptor extends ColumnDescriptor + { + private BinaryColumnDescriptor(ColumnImpl column, byte flags) + throws IOException + { + super(column, flags); + } + + @Override + protected void writeNonNullValue(Object value, ByteStream bout) + throws IOException + { + writeGeneralBinaryEntry( + ColumnImpl.toByteArray(value), isAscending(), bout); + } + } + + /** * ColumnDescriptor for columns which we cannot currently write. */ diff --git a/src/main/java/com/healthmarketscience/jackcess/util/SimpleColumnMatcher.java b/src/main/java/com/healthmarketscience/jackcess/util/SimpleColumnMatcher.java index 35ecfbd..2fac7bf 100644 --- a/src/main/java/com/healthmarketscience/jackcess/util/SimpleColumnMatcher.java +++ b/src/main/java/com/healthmarketscience/jackcess/util/SimpleColumnMatcher.java @@ -21,6 +21,7 @@ USA package com.healthmarketscience.jackcess.util; import java.io.IOException; +import java.util.Arrays; import com.healthmarketscience.jackcess.DataType; import com.healthmarketscience.jackcess.Table; @@ -45,7 +46,7 @@ public class SimpleColumnMatcher implements ColumnMatcher { public boolean matches(Table table, String columnName, Object value1, Object value2) { - if(ObjectUtils.equals(value1, value2)) { + if(equals(value1, value2)) { return true; } @@ -59,7 +60,7 @@ public class SimpleColumnMatcher implements ColumnMatcher { Object internalV1 = ColumnImpl.toInternalValue(dataType, value1); Object internalV2 = ColumnImpl.toInternalValue(dataType, value2); - return ObjectUtils.equals(internalV1, internalV2); + return equals(internalV1, internalV2); } catch(IOException e) { // ignored, just go with the original result } @@ -67,4 +68,15 @@ public class SimpleColumnMatcher implements ColumnMatcher { return false; } + /** + * Returns {@code true} if the two objects are equal, handling {@code null} + * and objects of type {@code byte[]}. + */ + private static boolean equals(Object o1, Object o2) + { + return (ObjectUtils.equals(o1, o2) || + ((o1 instanceof byte[]) && (o2 instanceof byte[]) && + Arrays.equals((byte[])o1, (byte[])o2))); + } + } diff --git a/src/test/data/V2010/binIdxTestV2010.accdb b/src/test/data/V2010/binIdxTestV2010.accdb new file mode 100644 index 0000000000000000000000000000000000000000..be8c61f20bec1e21f0f12021af164efabf37e6c8 GIT binary patch literal 417792 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 xxtQd`Su&lA26e*wvv#q$6F literal 0 HcmV?d00001 diff --git a/src/test/java/com/healthmarketscience/jackcess/IndexTest.java b/src/test/java/com/healthmarketscience/jackcess/IndexTest.java index 95cb2be..20828d1 100644 --- a/src/test/java/com/healthmarketscience/jackcess/IndexTest.java +++ b/src/test/java/com/healthmarketscience/jackcess/IndexTest.java @@ -678,6 +678,45 @@ public class IndexTest extends TestCase { } } + public void testBinaryIndex() throws Exception + { + for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.BINARY_INDEX)) { + Database db = open(testDB); + + Table table = db.getTable("Test"); + + Index idx = table.getIndex("BinAscIdx"); + doTestBinaryIndex(idx, "BinAsc", false); + + idx = table.getIndex("BinDscIdx"); + doTestBinaryIndex(idx, "BinDsc", true); + + db.close(); + } + } + + private static void doTestBinaryIndex(Index idx, String colName, boolean forward) + throws Exception + { + IndexCursor ic = CursorBuilder.createCursor(idx); + + for(Row row : idx.getTable().getDefaultCursor().newIterable().setForward(forward)) { + int id = row.getInt("ID"); + byte[] data = row.getBytes(colName); + + boolean found = false; + for(Row idxRow : ic.newEntryIterable(data)) { + + assertTrue(Arrays.equals(data, idxRow.getBytes(colName))); + if(id == idxRow.getInt("ID")) { + found = true; + } + } + + assertTrue(found); + } + } + private void doCheckForeignKeyIndex(Table ta, Index ia, Table tb) throws Exception { diff --git a/src/test/java/com/healthmarketscience/jackcess/impl/JetFormatTest.java b/src/test/java/com/healthmarketscience/jackcess/impl/JetFormatTest.java index 4f86896..c73e777 100644 --- a/src/test/java/com/healthmarketscience/jackcess/impl/JetFormatTest.java +++ b/src/test/java/com/healthmarketscience/jackcess/impl/JetFormatTest.java @@ -48,7 +48,8 @@ public class JetFormatTest extends TestCase { UNSUPPORTED("unsupportedFieldsTest"), LINKED("linkerTest"), BLOB("testOle"), - CALC_FIELD("calcFieldTest"); + CALC_FIELD("calcFieldTest"), + BINARY_INDEX("binIdxTest"); private final String _basename; -- 2.39.5