diff options
author | James Ahlborn <jtahlborn@yahoo.com> | 2015-03-16 20:16:51 +0000 |
---|---|---|
committer | James Ahlborn <jtahlborn@yahoo.com> | 2015-03-16 20:16:51 +0000 |
commit | fad035e0b93b55502f54474343c9ac618a62e66d (patch) | |
tree | 8eea55ea3bae05ef79cea1d0bcd33d1ad79841f1 /src/main | |
parent | b60366623ebdd7ce72798cb7fc4e40fa8e0cacc8 (diff) | |
download | jackcess-fad035e0b93b55502f54474343c9ac618a62e66d.tar.gz jackcess-fad035e0b93b55502f54474343c9ac618a62e66d.zip |
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
Diffstat (limited to 'src/main')
3 files changed, 114 insertions, 38 deletions
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 @@ -1314,6 +1314,71 @@ public class IndexData { } /** + * 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. */ private static Entry createSpecialEntry(RowIdImpl rowId) { @@ -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,30 +1801,38 @@ 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. */ private final class ReadOnlyColumnDescriptor extends ColumnDescriptor 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))); + } + } |