Browse Source

use new general 97 index support

git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/a97_indexes@1312 f203690c-595d-4dc9-a70b-905162fa7fd2
tags/jackcess-3.5.0
James Ahlborn 4 years ago
parent
commit
f9202b4fab

+ 40
- 45
src/main/java/com/healthmarketscience/jackcess/impl/IndexCursorImpl.java View File

@@ -42,7 +42,7 @@ import org.apache.commons.logging.LogFactory;
*/
public class IndexCursorImpl extends CursorImpl implements IndexCursor
{
private static final Log LOG = LogFactory.getLog(IndexCursorImpl.class);
private static final Log LOG = LogFactory.getLog(IndexCursorImpl.class);

/** IndexDirHandler for forward traversal */
private final IndexDirHandler _forwardDirHandler =
@@ -68,7 +68,7 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
_index.initialize();
_entryCursor = entryCursor;
}
/**
* Creates an indexed cursor for the given table, narrowed to the given
* range.
@@ -76,7 +76,7 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
* Note, index based table traversal may not include all rows, as certain
* types of indexes do not include all entries (namely, some indexes ignore
* null entries, see {@link Index#shouldIgnoreNulls}).
*
*
* @param table the table over which this cursor will traverse
* @param index index for the table which will define traversal order as
* well as enhance certain lookups
@@ -98,14 +98,9 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
throw new IllegalArgumentException(
"Given index is not for given table: " + index + ", " + table);
}
if(!table.getFormat().INDEXES_SUPPORTED) {
throw new IllegalArgumentException(
"JetFormat " + table.getFormat() +
" does not currently support index lookups");
}
if(index.getIndexData().getUnsupportedReason() != null) {
throw new IllegalArgumentException(
"Given index " + index +
"Given index " + index +
" is not usable for indexed lookups due to " +
index.getIndexData().getUnsupportedReason());
}
@@ -115,7 +110,7 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
// init the column matcher appropriately for the index type
cursor.setColumnMatcher(null);
return cursor;
}
}

private Set<String> getIndexEntryPattern()
{
@@ -135,7 +130,7 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
}

@Override
public Row findRowByEntry(Object... entryValues)
public Row findRowByEntry(Object... entryValues)
throws IOException
{
if(findFirstRowByEntry(entryValues)) {
@@ -143,16 +138,16 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
}
return null;
}
@Override
public boolean findFirstRowByEntry(Object... entryValues)
throws IOException
public boolean findFirstRowByEntry(Object... entryValues)
throws IOException
{
PositionImpl curPos = _curPos;
PositionImpl prevPos = _prevPos;
boolean found = false;
try {
found = findFirstRowByEntryImpl(toRowValues(entryValues), true,
found = findFirstRowByEntryImpl(toRowValues(entryValues), true,
_columnMatcher);
return found;
} finally {
@@ -167,8 +162,8 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
}

@Override
public void findClosestRowByEntry(Object... entryValues)
throws IOException
public void findClosestRowByEntry(Object... entryValues)
throws IOException
{
PositionImpl curPos = _curPos;
PositionImpl prevPos = _prevPos;
@@ -189,8 +184,8 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
}

@Override
public boolean currentRowMatchesEntry(Object... entryValues)
throws IOException
public boolean currentRowMatchesEntry(Object... entryValues)
throws IOException
{
return currentRowMatchesEntryImpl(toRowValues(entryValues), _columnMatcher);
}
@@ -205,17 +200,17 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
toRowValues(iterBuilder.getEntryValues()),
iterBuilder.getColumnMatcher());
}
@Override
protected IndexDirHandler getDirHandler(boolean moveForward) {
return (moveForward ? _forwardDirHandler : _reverseDirHandler);
}
@Override
protected boolean isUpToDate() {
return(super.isUpToDate() && _entryCursor.isUpToDate());
}
@Override
protected void reset(boolean moveForward) {
_entryCursor.reset(moveForward);
@@ -259,7 +254,7 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
return super.findAnotherRowImpl(columnPattern, valuePattern, moveForward,
columnMatcher, rowValues);
}
// sweet, we can use our index
if(!findPotentialRow(rowValues, true)) {
return false;
@@ -274,14 +269,14 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
* match the given values. Caller manages save/restore on failure.
*
* @param rowValues the column values built from the index column values
* @param requireMatch whether or not an exact match is found
* @param requireMatch whether or not an exact match is desired
* @return {@code true} if a valid row was found with the given values,
* {@code false} if no row was found
*/
protected boolean findFirstRowByEntryImpl(Object[] rowValues,
boolean requireMatch,
ColumnMatcher columnMatcher)
throws IOException
ColumnMatcher columnMatcher)
throws IOException
{
if(!findPotentialRow(rowValues, requireMatch)) {
return false;
@@ -317,7 +312,7 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
// determine if the pattern columns exactly match the index columns
boolean exactColumnMatch = rowPattern.keySet().equals(
getIndexEntryPattern());
// there may be multiple rows which fit the pattern subset used by
// the index, so we need to keep checking until our index values no
// longer match
@@ -337,12 +332,12 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
}

} while(moveToAnotherRow(moveForward));
// none of the potential rows matched
return false;
}

private boolean currentRowMatchesEntryImpl(Object[] rowValues,
private boolean currentRowMatchesEntryImpl(Object[] rowValues,
ColumnMatcher columnMatcher)
throws IOException
{
@@ -353,7 +348,7 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor

Object patValue = rowValues[col.getColumnIndex()];

if((patValue == IndexData.MIN_VALUE) ||
if((patValue == IndexData.MIN_VALUE) ||
(patValue == IndexData.MAX_VALUE)) {
// all remaining entry values are "special" (used for partial lookups)
return true;
@@ -366,9 +361,9 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
}
}

return true;
return true;
}
private boolean findPotentialRow(Object[] rowValues, boolean requireMatch)
throws IOException
{
@@ -400,8 +395,8 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
}

@Override
protected boolean keepSearching(ColumnMatcher columnMatcher,
Object searchInfo)
protected boolean keepSearching(ColumnMatcher columnMatcher,
Object searchInfo)
throws IOException
{
if(searchInfo instanceof Object[]) {
@@ -420,7 +415,7 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
return _entryCursor.getIndexData().constructPartialIndexRowFromEntry(
IndexData.MIN_VALUE, entryValues);
}
@Override
protected PositionImpl findAnotherPosition(
RowState rowState, PositionImpl curPos, boolean moveForward)
@@ -451,7 +446,7 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
public abstract IndexData.Entry getAnotherEntry()
throws IOException;
}
/**
* Handles moving the table index cursor forward.
*/
@@ -469,7 +464,7 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
return _entryCursor.getNextEntry();
}
}
/**
* Handles moving the table index cursor backward.
*/
@@ -486,15 +481,15 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
public IndexData.Entry getAnotherEntry() throws IOException {
return _entryCursor.getPreviousEntry();
}
}
}
/**
* Value object which maintains the current position of an IndexCursor.
*/
private static final class IndexPosition extends PositionImpl
{
private final IndexData.Entry _entry;
private IndexPosition(IndexData.Entry entry) {
_entry = entry;
}
@@ -503,11 +498,11 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
public RowIdImpl getRowId() {
return getEntry().getRowId();
}
public IndexData.Entry getEntry() {
return _entry;
}
@Override
protected boolean equalsImpl(Object o) {
return getEntry().equals(((IndexPosition)o).getEntry());
@@ -525,7 +520,7 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor
private final class EntryIterator extends BaseIterator
{
private final Object[] _rowValues;
private EntryIterator(Collection<String> columnNames, Object[] rowValues,
ColumnMatcher columnMatcher)
{
@@ -541,9 +536,9 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor

@Override
protected boolean findNext() throws IOException {
return (moveToNextRow() &&
return (moveToNextRow() &&
currentRowMatchesEntryImpl(_rowValues, _colMatcher));
}
}
}

}

+ 26
- 2
src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java View File

@@ -1515,11 +1515,14 @@ public class IndexData {
case TEXT:
case MEMO:
ColumnImpl.SortOrder sortOrder = col.getTextSortOrder();
if(ColumnImpl.GENERAL_SORT_ORDER.equals(sortOrder)) {
return new GenTextColumnDescriptor(col, flags);
}
if(ColumnImpl.GENERAL_LEGACY_SORT_ORDER.equals(sortOrder)) {
return new GenLegTextColumnDescriptor(col, flags);
}
if(ColumnImpl.GENERAL_SORT_ORDER.equals(sortOrder)) {
return new GenTextColumnDescriptor(col, flags);
if(ColumnImpl.GENERAL_97_SORT_ORDER.equals(sortOrder)) {
return new Gen97TextColumnDescriptor(col, flags);
}
// unsupported sort order
setUnsupportedReason("unsupported collating sort order " + sortOrder +
@@ -1914,6 +1917,27 @@ public class IndexData {
}
}

/**
* ColumnDescriptor for "general 97" sort order text based columns.
*/
private static final class Gen97TextColumnDescriptor
extends ColumnDescriptor
{
private Gen97TextColumnDescriptor(ColumnImpl column, byte flags)
throws IOException
{
super(column, flags);
}

@Override
protected void writeNonNullValue(Object value, ByteStream bout)
throws IOException
{
General97IndexCodes.GEN_97_INSTANCE.writeNonNullIndexTextValue(
value, bout, isAscending());
}
}

/**
* ColumnDescriptor for guid columns.
*/

+ 51
- 50
src/test/java/com/healthmarketscience/jackcess/impl/IndexCodesTest.java View File

@@ -56,7 +56,7 @@ public class IndexCodesTest extends TestCase {
SPECIAL_CHARS.put('\'', "\\'");
SPECIAL_CHARS.put('\\', "\\\\");
}
public IndexCodesTest(String name) throws Exception {
super(name);
}
@@ -77,7 +77,7 @@ public class IndexCodesTest extends TestCase {
}
}

private static void checkIndexEntries(final TestDB testDB, Table t, Index index) throws Exception
public static void checkIndexEntries(final TestDB testDB, Table t, Index index) throws Exception
{
// index.initialize();
// System.out.println("Ind " + index);
@@ -94,14 +94,14 @@ public class IndexCodesTest extends TestCase {
} finally {
if(!success) {
System.out.println("CurPos: " + curPos);
System.out.println("Value: " + row + ": " +
System.out.println("Value: " + row + ": " +
toUnicodeStr(row.get("data")));
}
}
}
}
}
private static void findRow(final TestDB testDB, Table t, Index index,
Row expectedRow,
Cursor.Position expectedPos)
@@ -111,7 +111,7 @@ public class IndexCodesTest extends TestCase {
Cursor cursor = CursorBuilder.createCursor(index, idxRow, idxRow);

Cursor.Position startPos = cursor.getSavepoint().getCurrentPosition();
cursor.beforeFirst();
while(cursor.moveToNextRow()) {
Row row = cursor.getCurrentRow();
@@ -125,11 +125,12 @@ public class IndexCodesTest extends TestCase {

// TODO long rows not handled completely yet in V2010
// seems to truncate entry at 508 bytes with some trailing 2 byte seq
if(testDB.getExpectedFileFormat() == Database.FileFormat.V2010) {
if((testDB != null) &&
(testDB.getExpectedFileFormat() == Database.FileFormat.V2010)) {
String rowId = expectedRow.getString("name");
String tName = t.getName();
if(("Table11".equals(tName) || "Table11_desc".equals(tName)) &&
("row10".equals(rowId) || "row11".equals(rowId) ||
("row10".equals(rowId) || "row11".equals(rowId) ||
"row12".equals(rowId))) {
System.out.println(
"TODO long rows not handled completely yet in V2010: " + tName +
@@ -142,13 +143,13 @@ public class IndexCodesTest extends TestCase {
entryToString(startPos));
}

//////
//
// The code below is for use in reverse engineering index entries.
//
//////
public void testNothing() throws Exception {
// keep this so build doesn't fail if other tests are disabled
}
@@ -161,7 +162,7 @@ public class IndexCodesTest extends TestCase {
.addColumn(new ColumnBuilder("row", DataType.TEXT))
.addColumn(new ColumnBuilder("data", DataType.TEXT))
.toTable(db);
for(int i = 0; i < 256; ++i) {
String str = "AA" + ((char)i) + "AA";
t.addRow("row" + i, str);
@@ -182,7 +183,7 @@ public class IndexCodesTest extends TestCase {
(byte)42 + i, (short)53 + i, 13 * i,
(6.7d / i), null, null, true);
}
db.close();
}

@@ -212,10 +213,10 @@ public class IndexCodesTest extends TestCase {
.toTable(db);

char c = (char)0x3041; // crazy 7F 02 ... A0
char c2 = (char)0x30A2; // crazy 7F 02 ...
char c2 = (char)0x30A2; // crazy 7F 02 ...
char c3 = (char)0x2045; // inat 27 ... 1C
char c4 = (char)0x3043; // crazy 7F 03 ... A0
char c5 = (char)0x3046; // crazy 7F 04 ...
char c5 = (char)0x3046; // crazy 7F 04 ...
char c6 = (char)0x30F6; // crazy 7F 0D ... A0
char c7 = (char)0x3099; // unprint 03
char c8 = (char)0x0041; // A
@@ -232,13 +233,13 @@ public class IndexCodesTest extends TestCase {
// t = new TableBuilder("Table2")
// .addColumn(new ColumnBuilder("data", DataType.TEXT))
// .toTable(db);
// writeChars(0x0000, t);

// t = new TableBuilder("Table3")
// .addColumn(new ColumnBuilder("data", DataType.TEXT))
// .toTable(db);
// writeChars(0x0400, t);


@@ -255,13 +256,13 @@ public class IndexCodesTest extends TestCase {

Index ind = t.getIndexes().iterator().next();
((IndexImpl)ind).initialize();
System.out.println("Ind " + ind);

Cursor cursor = CursorBuilder.createCursor(ind);
while(cursor.moveToNextRow()) {
System.out.println("=======");
String entryStr =
String entryStr =
entryToString(cursor.getSavepoint().getCurrentPosition());
System.out.println("Entry Bytes: " + entryStr);
System.out.println("Value: " + cursor.getCurrentRow() + "; " +
@@ -316,10 +317,10 @@ public class IndexCodesTest extends TestCase {
System.out.println("Savepoint: " + cursor.getSavepoint());
System.out.println("Value: " + cursor.getCurrentRow());
}
db.close();
}
public void x_testReverseIsoMdb2010() throws Exception
{
Database db = open(Database.FileFormat.V2010, new File("/data2/jackcess_test/testAllIndexCodes3_2010.accdb"));
@@ -343,8 +344,8 @@ public class IndexCodesTest extends TestCase {
Map<Character,String[]> inat2Codes = new TreeMap<Character,String[]>();
Map<Character,String[]> inat2ExtraCodes = new TreeMap<Character,String[]>();
Map<Character,String[]> inat2CrazyCodes = new TreeMap<Character,String[]>();
Cursor cursor = CursorBuilder.createCursor(index);
while(cursor.moveToNextRow()) {
// System.out.println("=======");
@@ -377,14 +378,14 @@ public class IndexCodesTest extends TestCase {
handleInlineEntry(m.group(1), c, inlineCodes);

} else if(entryStr.contains("01 01 01 80")) {
// handle most unprintable codes
type = "UNPRINTABLE";
Matcher m = unprintPat.matcher(entryStr);
m.find();
handleUnprintableEntry(m.group(2), c, unprintCodes);

} else if(entryStr.contains("01 02 02") &&
} else if(entryStr.contains("01 02 02") &&
!entryStr.contains("FF 02 80 FF 80")) {

// handle chars w/ symbols
@@ -393,7 +394,7 @@ public class IndexCodesTest extends TestCase {
m.find();
handleInternationalEntry(m.group(1), m.group(2), c,
inatInlineCodes, inatExtraCodes);
} else if(entryStr.contains("0E 02 0E 02 0E 02 0E 02 01 02")) {

// handle chars w/ symbols
@@ -401,7 +402,7 @@ public class IndexCodesTest extends TestCase {
Matcher m = unprint2Pat.matcher(entryStr);
m.find();
handleUnprintable2Entry(m.group(1), c, unprint2Codes);
} else if(entryStr.contains("FF 02 80 FF 80")) {

type = "CRAZY_INAT";
@@ -415,8 +416,8 @@ public class IndexCodesTest extends TestCase {

// throw new RuntimeException("unhandled " + entryStr);
System.out.println("unhandled " + entryStr);
}
}
System.out.println("Type: " + type);
}

@@ -456,7 +457,7 @@ public class IndexCodesTest extends TestCase {
toByteString(extra));
continue;
}
chars = unprintCodes.get(cc);
if(chars != null) {
System.out.println("U" + toByteString(chars));
@@ -495,10 +496,10 @@ public class IndexCodesTest extends TestCase {
throw new RuntimeException("Unhandled char " + toUnicodeStr(c));
}
System.out.println("\n***END CODES");
db.close();
}
public void x_testReverseIsoMdb() throws Exception
{
Database db = open(Database.FileFormat.V2000, new File("/data2/jackcess_test/testAllIndexCodes3.mdb"));
@@ -522,8 +523,8 @@ public class IndexCodesTest extends TestCase {
Map<Character,String[]> inat2Codes = new TreeMap<Character,String[]>();
Map<Character,String[]> inat2ExtraCodes = new TreeMap<Character,String[]>();
Map<Character,String[]> inat2CrazyCodes = new TreeMap<Character,String[]>();
Cursor cursor = CursorBuilder.createCursor(index);
while(cursor.moveToNextRow()) {
// System.out.println("=======");
@@ -555,14 +556,14 @@ public class IndexCodesTest extends TestCase {
handleInlineEntry(m.group(1), c, inlineCodes);

} else if(entryStr.contains("01 01 01 80")) {
// handle most unprintable codes
type = "UNPRINTABLE";
Matcher m = unprintPat.matcher(entryStr);
m.find();
handleUnprintableEntry(m.group(2), c, unprintCodes);

} else if(entryStr.contains("01 02 02") &&
} else if(entryStr.contains("01 02 02") &&
!entryStr.contains("FF 02 80 FF 80")) {

// handle chars w/ symbols
@@ -571,7 +572,7 @@ public class IndexCodesTest extends TestCase {
m.find();
handleInternationalEntry(m.group(1), m.group(2), c,
inatInlineCodes, inatExtraCodes);
} else if(entryStr.contains("4A 4A 4A 4A 01 02")) {

// handle chars w/ symbols
@@ -579,7 +580,7 @@ public class IndexCodesTest extends TestCase {
Matcher m = unprint2Pat.matcher(entryStr);
m.find();
handleUnprintable2Entry(m.group(1), c, unprint2Codes);
} else if(entryStr.contains("FF 02 80 FF 80")) {

type = "CRAZY_INAT";
@@ -592,8 +593,8 @@ public class IndexCodesTest extends TestCase {
} else {

throw new RuntimeException("unhandled " + entryStr);
}
}
System.out.println("Type: " + type);
}

@@ -633,7 +634,7 @@ public class IndexCodesTest extends TestCase {
toByteString(extra));
continue;
}
chars = unprintCodes.get(cc);
if(chars != null) {
System.out.println("U" + toByteString(chars));
@@ -672,7 +673,7 @@ public class IndexCodesTest extends TestCase {
throw new RuntimeException("Unhandled char " + toUnicodeStr(c));
}
System.out.println("\n***END CODES");
db.close();
}

@@ -691,21 +692,21 @@ public class IndexCodesTest extends TestCase {
{
inlineCodes.put(c, entryCodes.trim().split(" "));
}
private static void handleUnprintableEntry(
String entryCodes, char c, Map<Character,String[]> unprintCodes)
throws Exception
{
unprintCodes.put(c, entryCodes.trim().split(" "));
}
private static void handleUnprintable2Entry(
String entryCodes, char c, Map<Character,String[]> unprintCodes)
throws Exception
{
unprintCodes.put(c, entryCodes.trim().split(" "));
}
private static void handleInternationalEntry(
String inlineCodes, String entryCodes, char c,
Map<Character,String[]> inatInlineCodes,
@@ -732,20 +733,20 @@ public class IndexCodesTest extends TestCase {
}
}

private static String toUnicodeStr(Object obj) throws Exception {
public static String toUnicodeStr(Object obj) throws Exception {
StringBuilder sb = new StringBuilder();
for(char c : obj.toString().toCharArray()) {
sb.append(toUnicodeStr(c)).append(" ");
}
return sb.toString();
}
private static String toUnicodeStr(char c) throws Exception {
String specialStr = SPECIAL_CHARS.get(c);
if(specialStr != null) {
return specialStr;
}
String digits = Integer.toHexString(c).toUpperCase();
while(digits.length() < 4) {
digits = "0" + digits;
@@ -769,7 +770,7 @@ public class IndexCodesTest extends TestCase {
}
return builder.toString();
}
public static String entryToString(Cursor.Position curPos)
throws Exception
{
@@ -783,5 +784,5 @@ public class IndexCodesTest extends TestCase {
return ByteUtil.toHexString(ByteBuffer.wrap(entryBytes),
0, entryBytes.length, false);
}
}

Loading…
Cancel
Save