git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@454 f203690c-595d-4dc9-a70b-905162fa7fd2tags/jackcess-1.2.0
@@ -5,3 +5,4 @@ James Ahlborn - Added support for NUMERIC data type | |||
Jon Iles - Added support for reading table definitions that span multiple pages | |||
James Schopp - added support for reading currency columns | |||
Patricia Donaldson - contributed RowFilter class | |||
Dan Rollo - added support for new DB file formats |
@@ -37,6 +37,7 @@ | |||
<name>Dan Rollo</name> | |||
<id>bhamail</id> | |||
<email>bhamail@users.sf.net</email> | |||
<organization>Composite Software, Inc.</organization> | |||
<timezone>-5</timezone> | |||
</developer> | |||
</developers> |
@@ -49,6 +49,13 @@ | |||
<organization>Health Market Science, Inc.</organization> | |||
<timezone>-5</timezone> | |||
</developer> | |||
<developer> | |||
<name>Dan Rollo</name> | |||
<id>bhamail</id> | |||
<email>bhamail@users.sf.net</email> | |||
<organization>Composite Software, Inc.</organization> | |||
<timezone>-5</timezone> | |||
</developer> | |||
</developers> | |||
<licenses> | |||
<license> |
@@ -308,6 +308,23 @@ public final class ByteUtil { | |||
} | |||
return true; | |||
} | |||
/** | |||
* Searches for a pattern of bytes in the given buffer starting at the | |||
* given offset. | |||
* @return the offset of the pattern if a match is found, -1 otherwise | |||
*/ | |||
public static int findRange(ByteBuffer buffer, int start, byte[] pattern) | |||
{ | |||
byte firstByte = pattern[0]; | |||
int limit = buffer.limit() - pattern.length; | |||
for(int i = start; i < limit; ++i) { | |||
if((firstByte == buffer.get(i)) && matchesRange(buffer, i, pattern)) { | |||
return i; | |||
} | |||
} | |||
return -1; | |||
} | |||
/** | |||
* Convert a byte buffer to a hexadecimal string for display |
@@ -168,9 +168,38 @@ public class Database | |||
private static final String CAT_COL_DATE_UPDATE = "DateUpdate"; | |||
/** System catalog column name of the flags column */ | |||
private static final String CAT_COL_FLAGS = "Flags"; | |||
/** System catalog column name of the properties column */ | |||
private static final String CAT_COL_PROPS = "LvProp"; | |||
/** Empty database template for creating new databases */ | |||
private static final String EMPTY_MDB = "com/healthmarketscience/jackcess/empty.mdb"; | |||
public static enum FileFormat { | |||
V1997(null, JetFormat.VERSION_3), // v97 is not supported, so no empty template is provided | |||
V2000("com/healthmarketscience/jackcess/empty.mdb", JetFormat.VERSION_4), | |||
V2003("com/healthmarketscience/jackcess/empty2003.mdb", JetFormat.VERSION_4), | |||
V2007("com/healthmarketscience/jackcess/empty2007.accdb", JetFormat.VERSION_5, ".accdb"); | |||
private final String _emptyFile; | |||
private final JetFormat _format; | |||
private final String _ext; | |||
private FileFormat(String emptyDBFile, JetFormat jetFormat) { | |||
this(emptyDBFile, jetFormat, ".mdb"); | |||
} | |||
private FileFormat(String emptyDBFile, JetFormat jetFormat, String ext) { | |||
_emptyFile = emptyDBFile; | |||
_format = jetFormat; | |||
_ext = ext; | |||
} | |||
public JetFormat getJetFormat() { return _format; } | |||
public String getFileExtension() { return _ext; } | |||
@Override | |||
public String toString() { return name() + ", jetFormat: " + getJetFormat(); } | |||
} | |||
/** Prefix for column or table names that are reserved words */ | |||
private static final String ESCAPE_PREFIX = "x"; | |||
/** Prefix that flags system tables */ | |||
@@ -183,6 +212,8 @@ public class Database | |||
private static final String TABLE_SYSTEM_RELATIONSHIPS = "MSysRelationships"; | |||
/** Name of the table that contains queries */ | |||
private static final String TABLE_SYSTEM_QUERIES = "MSysQueries"; | |||
/** Name of the table that contains queries */ | |||
private static final String OBJECT_NAME_DBPROPS = "MSysDb"; | |||
/** System object type for table definitions */ | |||
private static final Short TYPE_TABLE = (short) 1; | |||
/** System object type for query definitions */ | |||
@@ -269,6 +300,8 @@ public class Database | |||
private boolean _useBigIndex; | |||
/** optional error handler to use when row errors are encountered */ | |||
private ErrorHandler _dbErrorHandler; | |||
/** the file format of the database */ | |||
private FileFormat _fileFormat; | |||
/** | |||
* Open an existing Database. If the existing file is not writeable, the | |||
@@ -329,14 +362,14 @@ public class Database | |||
} | |||
return new Database(openChannel(mdbFile, | |||
(!mdbFile.canWrite() || readOnly)), | |||
autoSync); | |||
autoSync, null); | |||
} | |||
/** | |||
* Create a new Database | |||
* Create a new Access 2000 Database | |||
* <p> | |||
* Equivalent to: | |||
* {@code create(mdbFile, DEFAULT_AUTO_SYNC);} | |||
* {@code create(FileFormat.V2000, mdbFile, DEFAULT_AUTO_SYNC);} | |||
* | |||
* @param mdbFile Location to write the new database to. <b>If this file | |||
* already exists, it will be overwritten.</b> | |||
@@ -348,7 +381,29 @@ public class Database | |||
} | |||
/** | |||
* Create a new Database | |||
* Create a new Database for the given fileFormat | |||
* <p> | |||
* Equivalent to: | |||
* {@code create(fileFormat, mdbFile, DEFAULT_AUTO_SYNC);} | |||
* | |||
* @param fileFormat version of new database. | |||
* @param mdbFile Location to write the new database to. <b>If this file | |||
* already exists, it will be overwritten.</b> | |||
* | |||
* @see #create(File,boolean) | |||
*/ | |||
public static Database create(FileFormat fileFormat, File mdbFile) | |||
throws IOException | |||
{ | |||
return create(fileFormat, mdbFile, DEFAULT_AUTO_SYNC); | |||
} | |||
/** | |||
* Create a new Access 2000 Database | |||
* <p> | |||
* Equivalent to: | |||
* {@code create(FileFormat.V2000, mdbFile, DEFAULT_AUTO_SYNC);} | |||
* | |||
* @param mdbFile Location to write the new database to. <b>If this file | |||
* already exists, it will be overwritten.</b> | |||
* @param autoSync whether or not to enable auto-syncing on write. if | |||
@@ -362,19 +417,53 @@ public class Database | |||
*/ | |||
public static Database create(File mdbFile, boolean autoSync) | |||
throws IOException | |||
{ | |||
{ | |||
return create(FileFormat.V2000, mdbFile, autoSync); | |||
} | |||
/** | |||
* Create a new Database for the given fileFormat | |||
* @param fileFormat version of new database. | |||
* @param mdbFile Location to write the new database to. <b>If this file | |||
* already exists, it will be overwritten.</b> | |||
* @param autoSync whether or not to enable auto-syncing on write. if | |||
* {@code true}, writes will be immediately flushed to disk. | |||
* This leaves the database in a (fairly) consistent state | |||
* on each write, but can be very inefficient for many | |||
* updates. if {@code false}, flushing to disk happens at | |||
* the jvm's leisure, which can be much faster, but may | |||
* leave the database in an inconsistent state if failures | |||
* are encountered during writing. | |||
*/ | |||
public static Database create(FileFormat fileFormat, File mdbFile, | |||
boolean autoSync) | |||
throws IOException | |||
{ | |||
FileChannel channel = openChannel(mdbFile, false); | |||
channel.truncate(0); | |||
channel.transferFrom(Channels.newChannel( | |||
Thread.currentThread().getContextClassLoader().getResourceAsStream( | |||
EMPTY_MDB)), 0, Integer.MAX_VALUE); | |||
return new Database(channel, autoSync); | |||
fileFormat._emptyFile)), 0, Integer.MAX_VALUE); | |||
return new Database(channel, autoSync, fileFormat); | |||
} | |||
private static FileChannel openChannel(File mdbFile, boolean readOnly) | |||
/** | |||
* Package visible only to support unit tests via DatabaseTest.openChannel(). | |||
* @param mdbFile file to open | |||
* @param readOnly true if read-only | |||
* @return a FileChannel on the given file. | |||
* @exception FileNotFoundException | |||
* if the mode is <tt>"r"</tt> but the given file object does | |||
* not denote an existing regular file, or if the mode begins | |||
* with <tt>"rw"</tt> but the given file object does not denote | |||
* an existing, writable regular file and a new regular file of | |||
* that name cannot be created, or if some other error occurs | |||
* while opening or creating the file | |||
*/ | |||
static FileChannel openChannel(final File mdbFile, final boolean readOnly) | |||
throws FileNotFoundException | |||
{ | |||
String mode = (readOnly ? "r" : "rw"); | |||
final String mode = (readOnly ? "r" : "rw"); | |||
return new RandomAccessFile(mdbFile, mode).getChannel(); | |||
} | |||
@@ -384,9 +473,12 @@ public class Database | |||
* FileChannel instead of a ReadableByteChannel because we need to | |||
* randomly jump around to various points in the file. | |||
*/ | |||
protected Database(FileChannel channel, boolean autoSync) throws IOException | |||
protected Database(FileChannel channel, boolean autoSync, | |||
FileFormat fileFormat) | |||
throws IOException | |||
{ | |||
_format = JetFormat.getFormat(channel); | |||
_fileFormat = fileFormat; | |||
_pageChannel = new PageChannel(channel, _format, autoSync); | |||
// note, it's slighly sketchy to pass ourselves along partially | |||
// constructed, but only our _format and _pageChannel refs should be | |||
@@ -449,6 +541,59 @@ public class Database | |||
public void setErrorHandler(ErrorHandler newErrorHandler) { | |||
_dbErrorHandler = newErrorHandler; | |||
} | |||
/** | |||
* Returns the FileFormat of this database (which may involve inspecting the | |||
* database itself). | |||
* @throws IllegalStateException if the file format cannot be determined | |||
*/ | |||
public FileFormat getFileFormat() | |||
{ | |||
if(_fileFormat == null) { | |||
Map<Database.FileFormat,byte[]> possibleFileFormats = | |||
getFormat().getPossibleFileFormats(); | |||
if(possibleFileFormats.size() == 1) { | |||
// single possible format, easy enough | |||
_fileFormat = possibleFileFormats.keySet().iterator().next(); | |||
} else { | |||
// need to check the "AccessVersion" property | |||
byte[] dbProps = null; | |||
for(Map<String,Object> row : | |||
Cursor.createCursor(_systemCatalog).iterable( | |||
Arrays.asList(CAT_COL_NAME, CAT_COL_PROPS))) { | |||
if(OBJECT_NAME_DBPROPS.equals(row.get(CAT_COL_NAME))) { | |||
dbProps = (byte[])row.get(CAT_COL_PROPS); | |||
break; | |||
} | |||
} | |||
if(dbProps != null) { | |||
// search for certain "version strings" in the properties (we | |||
// can't fully parse the properties objects, but we can still | |||
// find the byte pattern) | |||
ByteBuffer dbPropBuf = ByteBuffer.wrap(dbProps); | |||
for(Map.Entry<Database.FileFormat,byte[]> possible : | |||
possibleFileFormats.entrySet()) { | |||
if(ByteUtil.findRange(dbPropBuf, 0, possible.getValue()) >= 0) { | |||
_fileFormat = possible.getKey(); | |||
break; | |||
} | |||
} | |||
} | |||
if(_fileFormat == null) { | |||
throw new IllegalStateException("Could not determine FileFormat"); | |||
} | |||
} | |||
} | |||
return _fileFormat; | |||
} | |||
/** | |||
* Read the system catalog |
@@ -1540,17 +1540,31 @@ public abstract class Index implements Comparable<Index> { | |||
boolean isNegative = ((valueBytes[0] & 0x80) != 0); | |||
// bit twiddling rules: | |||
// isAsc && !isNeg => setReverseSignByte | |||
// isAsc && isNeg => flipBytes, setReverseSignByte | |||
// !isAsc && !isNeg => flipBytes, setReverseSignByte | |||
// !isAsc && isNeg => setReverseSignByte | |||
// isAsc && !isNeg => setReverseSignByte => FF 00 00 ... | |||
// isAsc && isNeg => flipBytes, setReverseSignByte => 00 FF FF ... | |||
// !isAsc && !isNeg => flipBytes, setReverseSignByte => FF FF FF ... | |||
// !isAsc && isNeg => setReverseSignByte => 00 00 00 ... | |||
// v2007 bit twiddling rules (old ordering was a bug, MS kb 837148): | |||
// isAsc && !isNeg => setSignByte 0xFF => FF 00 00 ... | |||
// isAsc && isNeg => setSignByte 0xFF, flipBytes => 00 FF FF ... | |||
// !isAsc && !isNeg => setSignByte 0xFF => FF 00 00 ... | |||
// !isAsc && isNeg => setSignByte 0xFF, flipBytes => 00 FF FF ... | |||
boolean alwaysRevFirstByte = getColumn().getFormat().REVERSE_FIRST_BYTE_IN_DESC_NUMERIC_INDEXES; | |||
if(alwaysRevFirstByte) { | |||
// reverse the sign byte (before any byte flipping) | |||
valueBytes[0] = (byte)0xFF; | |||
} | |||
if(isNegative == isAscending()) { | |||
flipBytes(valueBytes); | |||
} | |||
// reverse the sign byte (after any previous byte flipping) | |||
valueBytes[0] = (isNegative ? (byte)0x00 : (byte)0xFF); | |||
if(!alwaysRevFirstByte) { | |||
// reverse the sign byte (after any previous byte flipping) | |||
valueBytes[0] = (isNegative ? (byte)0x00 : (byte)0xFF); | |||
} | |||
bout.write(valueBytes); | |||
} |
@@ -31,6 +31,9 @@ import java.io.IOException; | |||
import java.nio.ByteBuffer; | |||
import java.nio.channels.FileChannel; | |||
import java.nio.charset.Charset; | |||
import java.util.Collections; | |||
import java.util.EnumMap; | |||
import java.util.Map; | |||
/** | |||
* Encapsulates constants describing a specific version of the Access Jet format | |||
@@ -46,12 +49,41 @@ public abstract class JetFormat { | |||
/** Maximum size of a text field */ | |||
public static final short TEXT_FIELD_MAX_LENGTH = 255 * TEXT_FIELD_UNIT_SIZE; | |||
/** Offset in the file that holds the byte describing the Jet format version */ | |||
/** Offset in the file that holds the byte describing the Jet format | |||
version */ | |||
private static final long OFFSET_VERSION = 20L; | |||
/** Version code for Jet version 3 */ | |||
private static final byte CODE_VERSION_3 = 0x0; | |||
/** Version code for Jet version 4 */ | |||
private static final byte CODE_VERSION_4 = 0x1; | |||
/** Version code for Jet version 5 */ | |||
private static final byte CODE_VERSION_5 = 0x2; | |||
/** value of the "AccessVersion" property for access 2000 dbs: | |||
{@code "08.50"} */ | |||
private static final byte[] ACCESS_VERSION_2000 = new byte[] { | |||
'0', 0, '8', 0, '.', 0, '5', 0, '0', 0}; | |||
/** value of the "AccessVersion" property for access 2002/2003 dbs | |||
{@code "09.50"} */ | |||
private static final byte[] ACCESS_VERSION_2003 = new byte[] { | |||
'0', 0, '9', 0, '.', 0, '5', 0, '0', 0}; | |||
// use nested inner class to avoid problematic static init loops | |||
private static final class PossibleFileFormats { | |||
private static final Map<Database.FileFormat,byte[]> POSSIBLE_VERSION_3 = | |||
Collections.singletonMap(Database.FileFormat.V1997, (byte[])null); | |||
private static final Map<Database.FileFormat,byte[]> POSSIBLE_VERSION_4 = | |||
new EnumMap<Database.FileFormat,byte[]>(Database.FileFormat.class); | |||
private static final Map<Database.FileFormat,byte[]> POSSIBLE_VERSION_5 = | |||
Collections.singletonMap(Database.FileFormat.V2007, (byte[])null); | |||
static { | |||
POSSIBLE_VERSION_4.put(Database.FileFormat.V2000, ACCESS_VERSION_2000); | |||
POSSIBLE_VERSION_4.put(Database.FileFormat.V2003, ACCESS_VERSION_2003); | |||
} | |||
} | |||
//These constants are populated by this class's constructor. They can't be | |||
//populated by the subclass's constructor because they are final, and Java | |||
@@ -128,13 +160,19 @@ public abstract class JetFormat { | |||
public final int MAX_TABLE_NAME_LENGTH; | |||
public final int MAX_COLUMN_NAME_LENGTH; | |||
public final int MAX_INDEX_NAME_LENGTH; | |||
public final boolean REVERSE_FIRST_BYTE_IN_DESC_NUMERIC_INDEXES; | |||
public final Charset CHARSET; | |||
public static final JetFormat VERSION_3 = new Jet3Format(); | |||
public static final JetFormat VERSION_4 = new Jet4Format(); | |||
public static final JetFormat VERSION_5 = new Jet5Format(); | |||
/** | |||
* @param channel the database file. | |||
* @return The Jet Format represented in the passed-in file | |||
* @throws IOException if the database file format is unsupported. | |||
*/ | |||
public static JetFormat getFormat(FileChannel channel) throws IOException { | |||
ByteBuffer buffer = ByteBuffer.allocate(1); | |||
@@ -144,8 +182,12 @@ public abstract class JetFormat { | |||
} | |||
buffer.flip(); | |||
byte version = buffer.get(); | |||
if (version == CODE_VERSION_4) { | |||
if (version == CODE_VERSION_3) { | |||
return VERSION_3; | |||
} else if (version == CODE_VERSION_4) { | |||
return VERSION_4; | |||
} else if (version == CODE_VERSION_5) { | |||
return VERSION_5; | |||
} | |||
throw new IOException("Unsupported version: " + version); | |||
} | |||
@@ -220,7 +262,8 @@ public abstract class JetFormat { | |||
MAX_TABLE_NAME_LENGTH = defineMaxTableNameLength(); | |||
MAX_COLUMN_NAME_LENGTH = defineMaxColumnNameLength(); | |||
MAX_INDEX_NAME_LENGTH = defineMaxIndexNameLength(); | |||
REVERSE_FIRST_BYTE_IN_DESC_NUMERIC_INDEXES = defineReverseFirstByteInDescNumericIndexes(); | |||
CHARSET = defineCharset(); | |||
} | |||
@@ -295,15 +338,23 @@ public abstract class JetFormat { | |||
protected abstract Charset defineCharset(); | |||
protected abstract boolean defineReverseFirstByteInDescNumericIndexes(); | |||
protected abstract Map<Database.FileFormat,byte[]> getPossibleFileFormats(); | |||
@Override | |||
public String toString() { | |||
return _name; | |||
} | |||
private static final class Jet4Format extends JetFormat { | |||
private static class Jet4Format extends JetFormat { | |||
private Jet4Format() { | |||
super("VERSION_4"); | |||
this("VERSION_4"); | |||
} | |||
private Jet4Format(final String name) { | |||
super(name); | |||
} | |||
@Override | |||
@@ -434,8 +485,44 @@ public abstract class JetFormat { | |||
@Override | |||
protected int defineMaxIndexNameLength() { return 64; } | |||
@Override | |||
protected boolean defineReverseFirstByteInDescNumericIndexes() { return false; } | |||
@Override | |||
protected Charset defineCharset() { return Charset.forName("UTF-16LE"); } | |||
@Override | |||
protected Map<Database.FileFormat,byte[]> getPossibleFileFormats() | |||
{ | |||
return PossibleFileFormats.POSSIBLE_VERSION_4; | |||
} | |||
} | |||
private static final class Jet3Format extends Jet4Format { | |||
private Jet3Format() { | |||
super("VERSION_3"); | |||
} | |||
@Override | |||
protected Map<Database.FileFormat,byte[]> getPossibleFileFormats() { | |||
return PossibleFileFormats.POSSIBLE_VERSION_3; | |||
} | |||
} | |||
private static final class Jet5Format extends Jet4Format { | |||
private Jet5Format() { | |||
super("VERSION_5"); | |||
} | |||
@Override | |||
protected boolean defineReverseFirstByteInDescNumericIndexes() { return true; } | |||
@Override | |||
protected Map<Database.FileFormat,byte[]> getPossibleFileFormats() { | |||
return PossibleFileFormats.POSSIBLE_VERSION_5; | |||
} | |||
} | |||
} |
@@ -56,9 +56,9 @@ public class PageChannel implements Channel, Flushable { | |||
new byte[]{PageTypes.INVALID, (byte)0, (byte)0, (byte)0}; | |||
/** Global usage map always lives on page 1 */ | |||
private static final int PAGE_GLOBAL_USAGE_MAP = 1; | |||
static final int PAGE_GLOBAL_USAGE_MAP = 1; | |||
/** Global usage map always lives at row 0 */ | |||
private static final int ROW_GLOBAL_USAGE_MAP = 0; | |||
static final int ROW_GLOBAL_USAGE_MAP = 0; | |||
/** Channel containing the database */ | |||
private final FileChannel _channel; |
@@ -72,8 +72,11 @@ public class UsageMap | |||
/** the current handler implementation for reading/writing the specific | |||
usage map type. note, this may change over time. */ | |||
private Handler _handler; | |||
/** | |||
/** Error message prefix used when map type is unrecognized. */ | |||
static final String MSG_PREFIX_UNRECOGNIZED_MAP = "Unrecognized map type: "; | |||
/** | |||
* @param database database that contains this usage map | |||
* @param tableBuffer Buffer that contains this map's declaration | |||
* @param pageNum Page number that this usage map is contained in | |||
@@ -139,7 +142,7 @@ public class UsageMap | |||
} else if (mapType == MAP_TYPE_REFERENCE) { | |||
_handler = new ReferenceHandler(); | |||
} else { | |||
throw new IOException("Unrecognized map type: " + mapType); | |||
throw new IOException(MSG_PREFIX_UNRECOGNIZED_MAP + mapType); | |||
} | |||
} | |||
@@ -27,7 +27,6 @@ King of Prussia, PA 19406 | |||
package com.healthmarketscience.jackcess; | |||
import java.io.File; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Map; | |||
@@ -36,6 +35,7 @@ import java.util.Random; | |||
import junit.framework.TestCase; | |||
import static com.healthmarketscience.jackcess.DatabaseTest.*; | |||
import static com.healthmarketscience.jackcess.JetFormatTest.*; | |||
/** | |||
@@ -67,157 +67,160 @@ public class BigIndexTest extends TestCase { | |||
public void testComplexIndex() throws Exception | |||
{ | |||
// this file has an index with "compressed" entries and node pages | |||
Database db = open(new File("test/data/compIndexTest.mdb")); | |||
Table t = db.getTable("Table1"); | |||
Index index = t.getIndex("CD_AGENTE"); | |||
assertFalse(index.isInitialized()); | |||
assertEquals(512, countRows(t)); | |||
assertEquals(512, index.getEntryCount()); | |||
db.close(); | |||
for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.COMP_INDEX)) { | |||
// this file has an index with "compressed" entries and node pages | |||
Database db = open(testDB); | |||
Table t = db.getTable("Table1"); | |||
Index index = t.getIndex("CD_AGENTE"); | |||
assertFalse(index.isInitialized()); | |||
assertEquals(512, countRows(t)); | |||
assertEquals(512, index.getEntryCount()); | |||
db.close(); | |||
} | |||
} | |||
public void testBigIndex() throws Exception | |||
{ | |||
for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.BIG_INDEX)) { | |||
// this file has an index with "compressed" entries and node pages | |||
File origFile = new File("test/data/bigIndexTest.mdb"); | |||
Database db = open(origFile); | |||
Table t = db.getTable("Table1"); | |||
Index index = t.getIndex("col1"); | |||
assertFalse(index.isInitialized()); | |||
assertEquals(0, countRows(t)); | |||
assertEquals(0, index.getEntryCount()); | |||
db.close(); | |||
DatabaseTest._autoSync = false; | |||
try { | |||
String extraText = " some random text to fill out the index and make it fill up pages with lots of extra bytes so i will keep typing until i think that i probably have enough text in the index entry so that i do not need to add as many entries in order"; | |||
// copy to temp file and attempt to edit | |||
db = openCopy(origFile); | |||
t = db.getTable("Table1"); | |||
index = t.getIndex("col1"); | |||
System.out.println("BigIndexTest: Index type: " + index.getClass()); | |||
// add 2,000 (pseudo) random entries to the table | |||
Random rand = new Random(13L); | |||
for(int i = 0; i < 2000; ++i) { | |||
if((i == 850) || (i == 1850)) { | |||
int end = i + 50; | |||
List<Object[]> rows = new ArrayList<Object[]>(50); | |||
for(; i < end; ++i) { | |||
Database db = open(testDB); | |||
Table t = db.getTable("Table1"); | |||
Index index = t.getIndex("col1"); | |||
assertFalse(index.isInitialized()); | |||
assertEquals(0, countRows(t)); | |||
assertEquals(0, index.getEntryCount()); | |||
db.close(); | |||
DatabaseTest._autoSync = false; | |||
try { | |||
String extraText = " some random text to fill out the index and make it fill up pages with lots of extra bytes so i will keep typing until i think that i probably have enough text in the index entry so that i do not need to add as many entries in order"; | |||
// copy to temp file and attempt to edit | |||
db = openCopy(testDB); | |||
t = db.getTable("Table1"); | |||
index = t.getIndex("col1"); | |||
System.out.println("BigIndexTest: Index type: " + index.getClass()); | |||
// add 2,000 (pseudo) random entries to the table | |||
Random rand = new Random(13L); | |||
for(int i = 0; i < 2000; ++i) { | |||
if((i == 850) || (i == 1850)) { | |||
int end = i + 50; | |||
List<Object[]> rows = new ArrayList<Object[]>(50); | |||
for(; i < end; ++i) { | |||
int nextInt = rand.nextInt(Integer.MAX_VALUE); | |||
String nextVal = "" + nextInt + extraText; | |||
if(((i + 1) % 333) == 0) { | |||
nextVal = null; | |||
} | |||
rows.add(new Object[]{nextVal, | |||
"this is some row data " + nextInt}); | |||
} | |||
t.addRows(rows); | |||
--i; | |||
} else { | |||
int nextInt = rand.nextInt(Integer.MAX_VALUE); | |||
String nextVal = "" + nextInt + extraText; | |||
if(((i + 1) % 333) == 0) { | |||
nextVal = null; | |||
} | |||
rows.add(new Object[]{nextVal, | |||
"this is some row data " + nextInt}); | |||
t.addRow(nextVal, "this is some row data " + nextInt); | |||
} | |||
t.addRows(rows); | |||
--i; | |||
} else { | |||
int nextInt = rand.nextInt(Integer.MAX_VALUE); | |||
String nextVal = "" + nextInt + extraText; | |||
if(((i + 1) % 333) == 0) { | |||
nextVal = null; | |||
} | |||
((BigIndex)index).validate(); | |||
db.flush(); | |||
t = db.getTable("Table1"); | |||
index = t.getIndex("col1"); | |||
// make sure all entries are there and correctly ordered | |||
String firstValue = " "; | |||
String prevValue = firstValue; | |||
int rowCount = 0; | |||
List<String> firstTwo = new ArrayList<String>(); | |||
for(Map<String,Object> row : Cursor.createIndexCursor(t, index)) { | |||
String origVal = (String)row.get("col1"); | |||
String val = origVal; | |||
if(val == null) { | |||
val = firstValue; | |||
} | |||
t.addRow(nextVal, "this is some row data " + nextInt); | |||
assertTrue("" + prevValue + " <= " + val + " " + rowCount, | |||
prevValue.compareTo(val) <= 0); | |||
if(firstTwo.size() < 2) { | |||
firstTwo.add(origVal); | |||
} | |||
prevValue = val; | |||
++rowCount; | |||
} | |||
} | |||
((BigIndex)index).validate(); | |||
db.flush(); | |||
t = db.getTable("Table1"); | |||
index = t.getIndex("col1"); | |||
// make sure all entries are there and correctly ordered | |||
String firstValue = " "; | |||
String prevValue = firstValue; | |||
int rowCount = 0; | |||
List<String> firstTwo = new ArrayList<String>(); | |||
for(Map<String,Object> row : Cursor.createIndexCursor(t, index)) { | |||
String origVal = (String)row.get("col1"); | |||
String val = origVal; | |||
if(val == null) { | |||
val = firstValue; | |||
assertEquals(2000, rowCount); | |||
((BigIndex)index).validate(); | |||
// delete an entry in the middle | |||
Cursor cursor = Cursor.createIndexCursor(t, index); | |||
for(int i = 0; i < (rowCount / 2); ++i) { | |||
assertTrue(cursor.moveToNextRow()); | |||
} | |||
assertTrue("" + prevValue + " <= " + val + " " + rowCount, | |||
prevValue.compareTo(val) <= 0); | |||
if(firstTwo.size() < 2) { | |||
firstTwo.add(origVal); | |||
cursor.deleteCurrentRow(); | |||
--rowCount; | |||
// remove all but the first two entries (from the end) | |||
cursor.afterLast(); | |||
for(int i = 0; i < (rowCount - 2); ++i) { | |||
assertTrue(cursor.moveToPreviousRow()); | |||
cursor.deleteCurrentRow(); | |||
} | |||
prevValue = val; | |||
++rowCount; | |||
} | |||
assertEquals(2000, rowCount); | |||
((BigIndex)index).validate(); | |||
((BigIndex)index).validate(); | |||
// delete an entry in the middle | |||
Cursor cursor = Cursor.createIndexCursor(t, index); | |||
for(int i = 0; i < (rowCount / 2); ++i) { | |||
assertTrue(cursor.moveToNextRow()); | |||
} | |||
cursor.deleteCurrentRow(); | |||
--rowCount; | |||
// remove all but the first two entries (from the end) | |||
cursor.afterLast(); | |||
for(int i = 0; i < (rowCount - 2); ++i) { | |||
assertTrue(cursor.moveToPreviousRow()); | |||
cursor.deleteCurrentRow(); | |||
} | |||
List<String> found = new ArrayList<String>(); | |||
for(Map<String,Object> row : Cursor.createIndexCursor(t, index)) { | |||
found.add((String)row.get("col1")); | |||
} | |||
((BigIndex)index).validate(); | |||
List<String> found = new ArrayList<String>(); | |||
for(Map<String,Object> row : Cursor.createIndexCursor(t, index)) { | |||
found.add((String)row.get("col1")); | |||
} | |||
assertEquals(firstTwo, found); | |||
assertEquals(firstTwo, found); | |||
// remove remaining entries | |||
cursor = Cursor.createCursor(t); | |||
for(int i = 0; i < 2; ++i) { | |||
assertTrue(cursor.moveToNextRow()); | |||
cursor.deleteCurrentRow(); | |||
} | |||
// remove remaining entries | |||
cursor = Cursor.createCursor(t); | |||
for(int i = 0; i < 2; ++i) { | |||
assertTrue(cursor.moveToNextRow()); | |||
cursor.deleteCurrentRow(); | |||
} | |||
assertFalse(cursor.moveToNextRow()); | |||
assertFalse(cursor.moveToPreviousRow()); | |||
((BigIndex)index).validate(); | |||
// add 50 (pseudo) random entries to the table | |||
rand = new Random(42L); | |||
for(int i = 0; i < 50; ++i) { | |||
int nextInt = rand.nextInt(Integer.MAX_VALUE); | |||
String nextVal = "some prefix " + nextInt + extraText; | |||
if(((i + 1) % 3333) == 0) { | |||
nextVal = null; | |||
} | |||
t.addRow(nextVal, "this is some row data " + nextInt); | |||
} | |||
((BigIndex)index).validate(); | |||
assertFalse(cursor.moveToNextRow()); | |||
assertFalse(cursor.moveToPreviousRow()); | |||
((BigIndex)index).validate(); | |||
// add 50 (pseudo) random entries to the table | |||
rand = new Random(42L); | |||
for(int i = 0; i < 50; ++i) { | |||
int nextInt = rand.nextInt(Integer.MAX_VALUE); | |||
String nextVal = "some prefix " + nextInt + extraText; | |||
if(((i + 1) % 3333) == 0) { | |||
nextVal = null; | |||
cursor = Cursor.createIndexCursor(t, index); | |||
while(cursor.moveToNextRow()) { | |||
cursor.deleteCurrentRow(); | |||
} | |||
t.addRow(nextVal, "this is some row data " + nextInt); | |||
} | |||
((BigIndex)index).validate(); | |||
((BigIndex)index).validate(); | |||
cursor = Cursor.createIndexCursor(t, index); | |||
while(cursor.moveToNextRow()) { | |||
cursor.deleteCurrentRow(); | |||
db.close(); | |||
} finally { | |||
DatabaseTest._autoSync = Database.DEFAULT_AUTO_SYNC; | |||
} | |||
((BigIndex)index).validate(); | |||
db.close(); | |||
} finally { | |||
DatabaseTest._autoSync = Database.DEFAULT_AUTO_SYNC; | |||
} | |||
} | |||
@@ -29,6 +29,8 @@ package com.healthmarketscience.jackcess; | |||
import junit.framework.TestCase; | |||
import static com.healthmarketscience.jackcess.JetFormatTest.*; | |||
/** | |||
* @author James Ahlborn | |||
*/ | |||
@@ -50,117 +52,119 @@ public class CursorBuilderTest extends TestCase { | |||
public void test() throws Exception | |||
{ | |||
Database db = CursorTest.createTestIndexTable(); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Cursor expected = Cursor.createCursor(table); | |||
Cursor found = new CursorBuilder(table).toCursor(); | |||
assertCursor(expected, found); | |||
expected = Cursor.createIndexCursor(table, idx); | |||
found = new CursorBuilder(table) | |||
.setIndex(idx) | |||
.toCursor(); | |||
assertCursor(expected, found); | |||
expected = Cursor.createIndexCursor(table, idx); | |||
found = new CursorBuilder(table) | |||
.setIndexByName("id") | |||
.toCursor(); | |||
assertCursor(expected, found); | |||
try { | |||
new CursorBuilder(table) | |||
.setIndexByName("foo"); | |||
fail("IllegalArgumentException should have been thrown"); | |||
} catch(IllegalArgumentException ignored) { | |||
// success | |||
} | |||
expected = Cursor.createIndexCursor(table, idx); | |||
found = new CursorBuilder(table) | |||
.setIndexByColumns(table.getColumn("id")) | |||
.toCursor(); | |||
assertCursor(expected, found); | |||
try { | |||
new CursorBuilder(table) | |||
.setIndexByColumns(table.getColumn("value")); | |||
fail("IllegalArgumentException should have been thrown"); | |||
} catch(IllegalArgumentException ignored) { | |||
// success | |||
} | |||
try { | |||
new CursorBuilder(table) | |||
.setIndexByColumns(table.getColumn("id"), table.getColumn("value")); | |||
fail("IllegalArgumentException should have been thrown"); | |||
} catch(IllegalArgumentException ignored) { | |||
// success | |||
for (final TestDB indexCursorDB : CursorTest.INDEX_CURSOR_DBS) { | |||
Database db = CursorTest.createTestIndexTable(indexCursorDB); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Cursor expected = Cursor.createCursor(table); | |||
Cursor found = new CursorBuilder(table).toCursor(); | |||
assertCursor(expected, found); | |||
expected = Cursor.createIndexCursor(table, idx); | |||
found = new CursorBuilder(table) | |||
.setIndex(idx) | |||
.toCursor(); | |||
assertCursor(expected, found); | |||
expected = Cursor.createIndexCursor(table, idx); | |||
found = new CursorBuilder(table) | |||
.setIndexByName("id") | |||
.toCursor(); | |||
assertCursor(expected, found); | |||
try { | |||
new CursorBuilder(table) | |||
.setIndexByName("foo"); | |||
fail("IllegalArgumentException should have been thrown"); | |||
} catch(IllegalArgumentException ignored) { | |||
// success | |||
} | |||
expected = Cursor.createIndexCursor(table, idx); | |||
found = new CursorBuilder(table) | |||
.setIndexByColumns(table.getColumn("id")) | |||
.toCursor(); | |||
assertCursor(expected, found); | |||
try { | |||
new CursorBuilder(table) | |||
.setIndexByColumns(table.getColumn("value")); | |||
fail("IllegalArgumentException should have been thrown"); | |||
} catch(IllegalArgumentException ignored) { | |||
// success | |||
} | |||
try { | |||
new CursorBuilder(table) | |||
.setIndexByColumns(table.getColumn("id"), table.getColumn("value")); | |||
fail("IllegalArgumentException should have been thrown"); | |||
} catch(IllegalArgumentException ignored) { | |||
// success | |||
} | |||
expected = Cursor.createCursor(table); | |||
expected.beforeFirst(); | |||
found = new CursorBuilder(table) | |||
.beforeFirst() | |||
.toCursor(); | |||
assertCursor(expected, found); | |||
expected = Cursor.createCursor(table); | |||
expected.afterLast(); | |||
found = new CursorBuilder(table) | |||
.afterLast() | |||
.toCursor(); | |||
assertCursor(expected, found); | |||
expected = Cursor.createCursor(table); | |||
expected.moveNextRows(2); | |||
Cursor.Savepoint sp = expected.getSavepoint(); | |||
found = new CursorBuilder(table) | |||
.afterLast() | |||
.restoreSavepoint(sp) | |||
.toCursor(); | |||
assertCursor(expected, found); | |||
expected = Cursor.createIndexCursor(table, idx); | |||
expected.moveNextRows(2); | |||
sp = expected.getSavepoint(); | |||
found = new CursorBuilder(table) | |||
.setIndex(idx) | |||
.beforeFirst() | |||
.restoreSavepoint(sp) | |||
.toCursor(); | |||
assertCursor(expected, found); | |||
expected = Cursor.createIndexCursor(table, idx, | |||
idx.constructIndexRowFromEntry(3), | |||
null); | |||
found = new CursorBuilder(table) | |||
.setIndex(idx) | |||
.setStartEntry(3) | |||
.toCursor(); | |||
assertCursor(expected, found); | |||
expected = Cursor.createIndexCursor(table, idx, | |||
idx.constructIndexRowFromEntry(3), | |||
false, | |||
idx.constructIndexRowFromEntry(7), | |||
false); | |||
found = new CursorBuilder(table) | |||
.setIndex(idx) | |||
.setStartEntry(3) | |||
.setStartRowInclusive(false) | |||
.setEndEntry(7) | |||
.setEndRowInclusive(false) | |||
.toCursor(); | |||
assertCursor(expected, found); | |||
db.close(); | |||
} | |||
expected = Cursor.createCursor(table); | |||
expected.beforeFirst(); | |||
found = new CursorBuilder(table) | |||
.beforeFirst() | |||
.toCursor(); | |||
assertCursor(expected, found); | |||
expected = Cursor.createCursor(table); | |||
expected.afterLast(); | |||
found = new CursorBuilder(table) | |||
.afterLast() | |||
.toCursor(); | |||
assertCursor(expected, found); | |||
expected = Cursor.createCursor(table); | |||
expected.moveNextRows(2); | |||
Cursor.Savepoint sp = expected.getSavepoint(); | |||
found = new CursorBuilder(table) | |||
.afterLast() | |||
.restoreSavepoint(sp) | |||
.toCursor(); | |||
assertCursor(expected, found); | |||
expected = Cursor.createIndexCursor(table, idx); | |||
expected.moveNextRows(2); | |||
sp = expected.getSavepoint(); | |||
found = new CursorBuilder(table) | |||
.setIndex(idx) | |||
.beforeFirst() | |||
.restoreSavepoint(sp) | |||
.toCursor(); | |||
assertCursor(expected, found); | |||
expected = Cursor.createIndexCursor(table, idx, | |||
idx.constructIndexRowFromEntry(3), | |||
null); | |||
found = new CursorBuilder(table) | |||
.setIndex(idx) | |||
.setStartEntry(3) | |||
.toCursor(); | |||
assertCursor(expected, found); | |||
expected = Cursor.createIndexCursor(table, idx, | |||
idx.constructIndexRowFromEntry(3), | |||
false, | |||
idx.constructIndexRowFromEntry(7), | |||
false); | |||
found = new CursorBuilder(table) | |||
.setIndex(idx) | |||
.setStartEntry(3) | |||
.setStartRowInclusive(false) | |||
.setEndEntry(7) | |||
.setEndRowInclusive(false) | |||
.toCursor(); | |||
assertCursor(expected, found); | |||
db.close(); | |||
} | |||
} |
@@ -27,7 +27,6 @@ King of Prussia, PA 19406 | |||
package com.healthmarketscience.jackcess; | |||
import java.io.File; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.Collections; | |||
@@ -37,7 +36,9 @@ import java.util.TreeSet; | |||
import junit.framework.TestCase; | |||
import static com.healthmarketscience.jackcess.Database.*; | |||
import static com.healthmarketscience.jackcess.DatabaseTest.*; | |||
import static com.healthmarketscience.jackcess.JetFormatTest.*; | |||
/** | |||
* @author James Ahlborn | |||
@@ -70,8 +71,8 @@ public class CursorTest extends TestCase { | |||
return expectedRows; | |||
} | |||
private static Database createTestTable() throws Exception { | |||
Database db = create(); | |||
private static Database createTestTable(final FileFormat fileFormat) throws Exception { | |||
Database db = create(fileFormat); | |||
Table table = new TableBuilder("test") | |||
.addColumn(new ColumnBuilder("id", DataType.LONG).toColumn()) | |||
@@ -96,16 +97,18 @@ public class CursorTest extends TestCase { | |||
} | |||
return expectedRows; | |||
} | |||
static Database createTestIndexTable() throws Exception { | |||
Database db = openCopy(new File("test/data/indexCursorTest.mdb")); | |||
static final TestDB[] INDEX_CURSOR_DBS = TestDB.getSupportedForBasename(Basename.INDEX_CURSOR); | |||
static Database createTestIndexTable(final TestDB indexCursorDB) throws Exception { | |||
Database db = openCopy(indexCursorDB); | |||
Table table = db.getTable("test"); | |||
for(Map<String,Object> row : createUnorderedTestTableData()) { | |||
table.addRow(row.get("id"), row.get("value")); | |||
} | |||
return db; | |||
} | |||
@@ -139,16 +142,18 @@ public class CursorTest extends TestCase { | |||
} | |||
public void testSimple() throws Exception { | |||
Database db = createTestTable(); | |||
for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) { | |||
Database db = createTestTable(fileFormat); | |||
Table table = db.getTable("test"); | |||
Cursor cursor = Cursor.createCursor(table); | |||
doTestSimple(table, cursor, null); | |||
db.close(); | |||
Table table = db.getTable("test"); | |||
Cursor cursor = Cursor.createCursor(table); | |||
doTestSimple(cursor, null); | |||
db.close(); | |||
} | |||
} | |||
private void doTestSimple(Table table, Cursor cursor, | |||
List<Map<String,Object>> expectedRows) | |||
private void doTestSimple(Cursor cursor, | |||
List<Map<String, Object>> expectedRows) | |||
throws Exception | |||
{ | |||
if(expectedRows == null) { | |||
@@ -164,17 +169,19 @@ public class CursorTest extends TestCase { | |||
} | |||
public void testMove() throws Exception { | |||
Database db = createTestTable(); | |||
for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) { | |||
Database db = createTestTable(fileFormat); | |||
Table table = db.getTable("test"); | |||
Cursor cursor = Cursor.createCursor(table); | |||
doTestMove(table, cursor, null); | |||
db.close(); | |||
Table table = db.getTable("test"); | |||
Cursor cursor = Cursor.createCursor(table); | |||
doTestMove(cursor, null); | |||
db.close(); | |||
} | |||
} | |||
private void doTestMove(Table table, Cursor cursor, | |||
List<Map<String,Object>> expectedRows) | |||
private void doTestMove(Cursor cursor, | |||
List<Map<String, Object>> expectedRows) | |||
throws Exception | |||
{ | |||
if(expectedRows == null) { | |||
@@ -221,13 +228,15 @@ public class CursorTest extends TestCase { | |||
} | |||
public void testSearch() throws Exception { | |||
Database db = createTestTable(); | |||
for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) { | |||
Database db = createTestTable(fileFormat); | |||
Table table = db.getTable("test"); | |||
Cursor cursor = Cursor.createCursor(table); | |||
doTestSearch(table, cursor, null, 42, -13); | |||
db.close(); | |||
Table table = db.getTable("test"); | |||
Cursor cursor = Cursor.createCursor(table); | |||
doTestSearch(table, cursor, null, 42, -13); | |||
db.close(); | |||
} | |||
} | |||
private void doTestSearch(Table table, Cursor cursor, Index index, | |||
@@ -303,17 +312,19 @@ public class CursorTest extends TestCase { | |||
} | |||
public void testReverse() throws Exception { | |||
Database db = createTestTable(); | |||
for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) { | |||
Database db = createTestTable(fileFormat); | |||
Table table = db.getTable("test"); | |||
Cursor cursor = Cursor.createCursor(table); | |||
doTestReverse(table, cursor, null); | |||
Table table = db.getTable("test"); | |||
Cursor cursor = Cursor.createCursor(table); | |||
doTestReverse(cursor, null); | |||
db.close(); | |||
db.close(); | |||
} | |||
} | |||
private void doTestReverse(Table table, Cursor cursor, | |||
List<Map<String,Object>> expectedRows) | |||
private void doTestReverse(Cursor cursor, | |||
List<Map<String, Object>> expectedRows) | |||
throws Exception | |||
{ | |||
if(expectedRows == null) { | |||
@@ -330,15 +341,17 @@ public class CursorTest extends TestCase { | |||
} | |||
public void testLiveAddition() throws Exception { | |||
Database db = createTestTable(); | |||
for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) { | |||
Database db = createTestTable(fileFormat); | |||
Table table = db.getTable("test"); | |||
Table table = db.getTable("test"); | |||
Cursor cursor1 = Cursor.createCursor(table); | |||
Cursor cursor2 = Cursor.createCursor(table); | |||
doTestLiveAddition(table, cursor1, cursor2, 11); | |||
db.close(); | |||
Cursor cursor1 = Cursor.createCursor(table); | |||
Cursor cursor2 = Cursor.createCursor(table); | |||
doTestLiveAddition(table, cursor1, cursor2, 11); | |||
db.close(); | |||
} | |||
} | |||
private void doTestLiveAddition(Table table, | |||
@@ -369,25 +382,27 @@ public class CursorTest extends TestCase { | |||
public void testLiveDeletion() throws Exception { | |||
Database db = createTestTable(); | |||
for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) { | |||
Database db = createTestTable(fileFormat); | |||
Table table = db.getTable("test"); | |||
Table table = db.getTable("test"); | |||
Cursor cursor1 = Cursor.createCursor(table); | |||
Cursor cursor2 = Cursor.createCursor(table); | |||
Cursor cursor3 = Cursor.createCursor(table); | |||
Cursor cursor4 = Cursor.createCursor(table); | |||
doTestLiveDeletion(table, cursor1, cursor2, cursor3, cursor4, 1); | |||
db.close(); | |||
Cursor cursor1 = Cursor.createCursor(table); | |||
Cursor cursor2 = Cursor.createCursor(table); | |||
Cursor cursor3 = Cursor.createCursor(table); | |||
Cursor cursor4 = Cursor.createCursor(table); | |||
doTestLiveDeletion(cursor1, cursor2, cursor3, cursor4, 1); | |||
db.close(); | |||
} | |||
} | |||
private void doTestLiveDeletion(Table table, | |||
Cursor cursor1, | |||
Cursor cursor2, | |||
Cursor cursor3, | |||
Cursor cursor4, | |||
int firstValue) throws Exception | |||
private void doTestLiveDeletion( | |||
Cursor cursor1, | |||
Cursor cursor2, | |||
Cursor cursor3, | |||
Cursor cursor4, | |||
int firstValue) throws Exception | |||
{ | |||
assertEquals(2, cursor1.moveNextRows(2)); | |||
assertEquals(3, cursor2.moveNextRows(3)); | |||
@@ -460,220 +475,246 @@ public class CursorTest extends TestCase { | |||
} | |||
public void testSimpleIndex() throws Exception { | |||
Database db = createTestIndexTable(); | |||
for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) { | |||
Database db = createTestIndexTable(indexCursorDB); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
assertTable(createUnorderedTestTableData(), table); | |||
assertTable(createUnorderedTestTableData(), table); | |||
Cursor cursor = Cursor.createIndexCursor(table, idx); | |||
doTestSimple(table, cursor, null); | |||
Cursor cursor = Cursor.createIndexCursor(table, idx); | |||
doTestSimple(cursor, null); | |||
db.close(); | |||
db.close(); | |||
} | |||
} | |||
public void testMoveIndex() throws Exception { | |||
Database db = createTestIndexTable(); | |||
for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) { | |||
Database db = createTestIndexTable(indexCursorDB); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Cursor cursor = Cursor.createIndexCursor(table, idx); | |||
doTestMove(table, cursor, null); | |||
db.close(); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Cursor cursor = Cursor.createIndexCursor(table, idx); | |||
doTestMove(cursor, null); | |||
db.close(); | |||
} | |||
} | |||
public void testReverseIndex() throws Exception { | |||
Database db = createTestIndexTable(); | |||
for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) { | |||
Database db = createTestIndexTable(indexCursorDB); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Cursor cursor = Cursor.createIndexCursor(table, idx); | |||
doTestReverse(table, cursor, null); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Cursor cursor = Cursor.createIndexCursor(table, idx); | |||
doTestReverse(cursor, null); | |||
db.close(); | |||
db.close(); | |||
} | |||
} | |||
public void testSearchIndex() throws Exception { | |||
Database db = createTestIndexTable(); | |||
for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) { | |||
Database db = createTestIndexTable(indexCursorDB); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Cursor cursor = Cursor.createIndexCursor(table, idx); | |||
doTestSearch(table, cursor, idx, 42, -13); | |||
db.close(); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Cursor cursor = Cursor.createIndexCursor(table, idx); | |||
doTestSearch(table, cursor, idx, 42, -13); | |||
db.close(); | |||
} | |||
} | |||
public void testLiveAdditionIndex() throws Exception { | |||
Database db = createTestIndexTable(); | |||
for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) { | |||
Database db = createTestIndexTable(indexCursorDB); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Cursor cursor1 = Cursor.createIndexCursor(table, idx); | |||
Cursor cursor2 = Cursor.createIndexCursor(table, idx); | |||
doTestLiveAddition(table, cursor1, cursor2, 11); | |||
db.close(); | |||
Cursor cursor1 = Cursor.createIndexCursor(table, idx); | |||
Cursor cursor2 = Cursor.createIndexCursor(table, idx); | |||
doTestLiveAddition(table, cursor1, cursor2, 11); | |||
db.close(); | |||
} | |||
} | |||
public void testLiveDeletionIndex() throws Exception { | |||
Database db = createTestIndexTable(); | |||
for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) { | |||
Database db = createTestIndexTable(indexCursorDB); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Cursor cursor1 = Cursor.createIndexCursor(table, idx); | |||
Cursor cursor2 = Cursor.createIndexCursor(table, idx); | |||
Cursor cursor3 = Cursor.createIndexCursor(table, idx); | |||
Cursor cursor4 = Cursor.createIndexCursor(table, idx); | |||
doTestLiveDeletion(table, cursor1, cursor2, cursor3, cursor4, 1); | |||
db.close(); | |||
Cursor cursor1 = Cursor.createIndexCursor(table, idx); | |||
Cursor cursor2 = Cursor.createIndexCursor(table, idx); | |||
Cursor cursor3 = Cursor.createIndexCursor(table, idx); | |||
Cursor cursor4 = Cursor.createIndexCursor(table, idx); | |||
doTestLiveDeletion(cursor1, cursor2, cursor3, cursor4, 1); | |||
db.close(); | |||
} | |||
} | |||
public void testSimpleIndexSubRange() throws Exception { | |||
for(int i = 0; i < 2; ++i) { | |||
Database db = createTestIndexTable(); | |||
for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) { | |||
for(int i = 0; i < 2; ++i) { | |||
Database db = createTestIndexTable(indexCursorDB); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Cursor cursor = createIndexSubRangeCursor(table, idx, i); | |||
Cursor cursor = createIndexSubRangeCursor(table, idx, i); | |||
List<Map<String,Object>> expectedRows = | |||
createTestTableData(3, 9); | |||
List<Map<String,Object>> expectedRows = | |||
createTestTableData(3, 9); | |||
doTestSimple(table, cursor, expectedRows); | |||
db.close(); | |||
doTestSimple(cursor, expectedRows); | |||
db.close(); | |||
} | |||
} | |||
} | |||
public void testMoveIndexSubRange() throws Exception { | |||
for(int i = 0; i < 2; ++i) { | |||
Database db = createTestIndexTable(); | |||
for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) { | |||
for(int i = 0; i < 2; ++i) { | |||
Database db = createTestIndexTable(indexCursorDB); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Cursor cursor = createIndexSubRangeCursor(table, idx, i); | |||
Cursor cursor = createIndexSubRangeCursor(table, idx, i); | |||
List<Map<String,Object>> expectedRows = | |||
createTestTableData(3, 9); | |||
List<Map<String,Object>> expectedRows = | |||
createTestTableData(3, 9); | |||
doTestMove(table, cursor, expectedRows); | |||
db.close(); | |||
doTestMove(cursor, expectedRows); | |||
db.close(); | |||
} | |||
} | |||
} | |||
public void testSearchIndexSubRange() throws Exception { | |||
for(int i = 0; i < 2; ++i) { | |||
Database db = createTestIndexTable(); | |||
for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) { | |||
for(int i = 0; i < 2; ++i) { | |||
Database db = createTestIndexTable(indexCursorDB); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Cursor cursor = createIndexSubRangeCursor(table, idx, i); | |||
Cursor cursor = createIndexSubRangeCursor(table, idx, i); | |||
doTestSearch(table, cursor, idx, 2, 9); | |||
db.close(); | |||
doTestSearch(table, cursor, idx, 2, 9); | |||
db.close(); | |||
} | |||
} | |||
} | |||
public void testReverseIndexSubRange() throws Exception { | |||
for(int i = 0; i < 2; ++i) { | |||
Database db = createTestIndexTable(); | |||
for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) { | |||
for(int i = 0; i < 2; ++i) { | |||
Database db = createTestIndexTable(indexCursorDB); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Cursor cursor = createIndexSubRangeCursor(table, idx, i); | |||
Cursor cursor = createIndexSubRangeCursor(table, idx, i); | |||
List<Map<String,Object>> expectedRows = | |||
createTestTableData(3, 9); | |||
List<Map<String,Object>> expectedRows = | |||
createTestTableData(3, 9); | |||
doTestReverse(table, cursor, expectedRows); | |||
doTestReverse(cursor, expectedRows); | |||
db.close(); | |||
db.close(); | |||
} | |||
} | |||
} | |||
public void testLiveAdditionIndexSubRange() throws Exception { | |||
for(int i = 0; i < 2; ++i) { | |||
Database db = createTestIndexTable(); | |||
for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) { | |||
for(int i = 0; i < 2; ++i) { | |||
Database db = createTestIndexTable(indexCursorDB); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Cursor cursor1 = createIndexSubRangeCursor(table, idx, i); | |||
Cursor cursor2 = createIndexSubRangeCursor(table, idx, i); | |||
Cursor cursor1 = createIndexSubRangeCursor(table, idx, i); | |||
Cursor cursor2 = createIndexSubRangeCursor(table, idx, i); | |||
doTestLiveAddition(table, cursor1, cursor2, 8); | |||
db.close(); | |||
doTestLiveAddition(table, cursor1, cursor2, 8); | |||
db.close(); | |||
} | |||
} | |||
} | |||
public void testLiveDeletionIndexSubRange() throws Exception { | |||
for(int i = 0; i < 2; ++i) { | |||
Database db = createTestIndexTable(); | |||
for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) { | |||
for(int i = 0; i < 2; ++i) { | |||
Database db = createTestIndexTable(indexCursorDB); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Cursor cursor1 = createIndexSubRangeCursor(table, idx, i); | |||
Cursor cursor2 = createIndexSubRangeCursor(table, idx, i); | |||
Cursor cursor3 = createIndexSubRangeCursor(table, idx, i); | |||
Cursor cursor4 = createIndexSubRangeCursor(table, idx, i); | |||
Cursor cursor1 = createIndexSubRangeCursor(table, idx, i); | |||
Cursor cursor2 = createIndexSubRangeCursor(table, idx, i); | |||
Cursor cursor3 = createIndexSubRangeCursor(table, idx, i); | |||
Cursor cursor4 = createIndexSubRangeCursor(table, idx, i); | |||
doTestLiveDeletion(table, cursor1, cursor2, cursor3, cursor4, 4); | |||
doTestLiveDeletion(cursor1, cursor2, cursor3, cursor4, 4); | |||
db.close(); | |||
} | |||
db.close(); | |||
} | |||
} | |||
} | |||
public void testId() throws Exception | |||
{ | |||
Database db = createTestIndexTable(); | |||
for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) { | |||
Database db = createTestIndexTable(indexCursorDB); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Table table = db.getTable("test"); | |||
Index idx = table.getIndexes().get(0); | |||
Cursor tCursor = Cursor.createCursor(table); | |||
Cursor iCursor = Cursor.createIndexCursor(table, idx); | |||
Cursor tCursor = Cursor.createCursor(table); | |||
Cursor iCursor = Cursor.createIndexCursor(table, idx); | |||
Cursor.Savepoint tSave = tCursor.getSavepoint(); | |||
Cursor.Savepoint iSave = iCursor.getSavepoint(); | |||
Cursor.Savepoint tSave = tCursor.getSavepoint(); | |||
Cursor.Savepoint iSave = iCursor.getSavepoint(); | |||
tCursor.restoreSavepoint(tSave); | |||
iCursor.restoreSavepoint(iSave); | |||
tCursor.restoreSavepoint(tSave); | |||
iCursor.restoreSavepoint(iSave); | |||
try { | |||
tCursor.restoreSavepoint(iSave); | |||
fail("IllegalArgumentException should have been thrown"); | |||
} catch(IllegalArgumentException e) { | |||
// success | |||
} | |||
try { | |||
tCursor.restoreSavepoint(iSave); | |||
fail("IllegalArgumentException should have been thrown"); | |||
} catch(IllegalArgumentException e) { | |||
// success | |||
} | |||
try { | |||
iCursor.restoreSavepoint(tSave); | |||
fail("IllegalArgumentException should have been thrown"); | |||
} catch(IllegalArgumentException e) { | |||
// success | |||
} | |||
try { | |||
iCursor.restoreSavepoint(tSave); | |||
fail("IllegalArgumentException should have been thrown"); | |||
} catch(IllegalArgumentException e) { | |||
// success | |||
} | |||
Cursor tCursor2 = Cursor.createCursor(table); | |||
Cursor iCursor2 = Cursor.createIndexCursor(table, idx); | |||
Cursor tCursor2 = Cursor.createCursor(table); | |||
Cursor iCursor2 = Cursor.createIndexCursor(table, idx); | |||
tCursor2.restoreSavepoint(tSave); | |||
iCursor2.restoreSavepoint(iSave); | |||
tCursor2.restoreSavepoint(tSave); | |||
iCursor2.restoreSavepoint(iSave); | |||
db.close(); | |||
db.close(); | |||
} | |||
} | |||
} |
@@ -35,6 +35,7 @@ import java.util.List; | |||
import junit.framework.TestCase; | |||
import static com.healthmarketscience.jackcess.Database.*; | |||
import static com.healthmarketscience.jackcess.DatabaseTest.*; | |||
/** | |||
@@ -49,94 +50,96 @@ public class ErrorHandlerTest extends TestCase | |||
public void testErrorHandler() throws Exception | |||
{ | |||
Database db = create(); | |||
Table table = | |||
new TableBuilder("test") | |||
.addColumn(new ColumnBuilder("col", DataType.TEXT).toColumn()) | |||
.addColumn(new ColumnBuilder("val", DataType.LONG).toColumn()) | |||
.toTable(db); | |||
table.addRow("row1", 1); | |||
table.addRow("row2", 2); | |||
table.addRow("row3", 3); | |||
assertTable(createExpectedTable( | |||
createExpectedRow("col", "row1", | |||
"val", 1), | |||
createExpectedRow("col", "row2", | |||
"val", 2), | |||
createExpectedRow("col", "row3", | |||
"val", 3)), | |||
table); | |||
replaceColumn(table, "val"); | |||
table.reset(); | |||
try { | |||
table.getNextRow(); | |||
fail("IOException should have been thrown"); | |||
} catch(IOException e) { | |||
// success | |||
} | |||
for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) { | |||
Database db = create(fileFormat); | |||
Table table = | |||
new TableBuilder("test") | |||
.addColumn(new ColumnBuilder("col", DataType.TEXT).toColumn()) | |||
.addColumn(new ColumnBuilder("val", DataType.LONG).toColumn()) | |||
.toTable(db); | |||
table.addRow("row1", 1); | |||
table.addRow("row2", 2); | |||
table.addRow("row3", 3); | |||
assertTable(createExpectedTable( | |||
createExpectedRow("col", "row1", | |||
"val", 1), | |||
createExpectedRow("col", "row2", | |||
"val", 2), | |||
createExpectedRow("col", "row3", | |||
"val", 3)), | |||
table); | |||
replaceColumn(table, "val"); | |||
table.reset(); | |||
try { | |||
table.getNextRow(); | |||
fail("IOException should have been thrown"); | |||
} catch(IOException e) { | |||
// success | |||
} | |||
table.reset(); | |||
table.setErrorHandler(new ReplacementErrorHandler()); | |||
assertTable(createExpectedTable( | |||
createExpectedRow("col", "row1", | |||
"val", null), | |||
createExpectedRow("col", "row2", | |||
"val", null), | |||
createExpectedRow("col", "row3", | |||
"val", null)), | |||
table); | |||
Cursor c1 = Cursor.createCursor(table); | |||
Cursor c2 = Cursor.createCursor(table); | |||
Cursor c3 = Cursor.createCursor(table); | |||
c2.setErrorHandler(new DebugErrorHandler("#error")); | |||
c3.setErrorHandler(Database.DEFAULT_ERROR_HANDLER); | |||
assertCursor(createExpectedTable( | |||
createExpectedRow("col", "row1", | |||
"val", null), | |||
createExpectedRow("col", "row2", | |||
"val", null), | |||
createExpectedRow("col", "row3", | |||
"val", null)), | |||
c1); | |||
assertCursor(createExpectedTable( | |||
createExpectedRow("col", "row1", | |||
"val", "#error"), | |||
createExpectedRow("col", "row2", | |||
"val", "#error"), | |||
createExpectedRow("col", "row3", | |||
"val", "#error")), | |||
c2); | |||
try { | |||
c3.getNextRow(); | |||
fail("IOException should have been thrown"); | |||
} catch(IOException e) { | |||
// success | |||
} | |||
table.reset(); | |||
table.setErrorHandler(new ReplacementErrorHandler()); | |||
assertTable(createExpectedTable( | |||
createExpectedRow("col", "row1", | |||
"val", null), | |||
createExpectedRow("col", "row2", | |||
"val", null), | |||
createExpectedRow("col", "row3", | |||
"val", null)), | |||
table); | |||
Cursor c1 = Cursor.createCursor(table); | |||
Cursor c2 = Cursor.createCursor(table); | |||
Cursor c3 = Cursor.createCursor(table); | |||
c2.setErrorHandler(new DebugErrorHandler("#error")); | |||
c3.setErrorHandler(Database.DEFAULT_ERROR_HANDLER); | |||
assertCursor(createExpectedTable( | |||
createExpectedRow("col", "row1", | |||
"val", null), | |||
createExpectedRow("col", "row2", | |||
"val", null), | |||
createExpectedRow("col", "row3", | |||
"val", null)), | |||
c1); | |||
assertCursor(createExpectedTable( | |||
createExpectedRow("col", "row1", | |||
"val", "#error"), | |||
createExpectedRow("col", "row2", | |||
"val", "#error"), | |||
createExpectedRow("col", "row3", | |||
"val", "#error")), | |||
c2); | |||
try { | |||
c3.getNextRow(); | |||
fail("IOException should have been thrown"); | |||
} catch(IOException e) { | |||
// success | |||
} | |||
table.setErrorHandler(null); | |||
c1.setErrorHandler(null); | |||
c1.reset(); | |||
try { | |||
c1.getNextRow(); | |||
fail("IOException should have been thrown"); | |||
} catch(IOException e) { | |||
// success | |||
} | |||
table.setErrorHandler(null); | |||
c1.setErrorHandler(null); | |||
c1.reset(); | |||
try { | |||
c1.getNextRow(); | |||
fail("IOException should have been thrown"); | |||
} catch(IOException e) { | |||
// success | |||
} | |||
db.close(); | |||
db.close(); | |||
} | |||
} | |||
@SuppressWarnings("unchecked") |
@@ -39,6 +39,7 @@ import java.util.List; | |||
import junit.framework.TestCase; | |||
import static com.healthmarketscience.jackcess.Database.*; | |||
import static com.healthmarketscience.jackcess.DatabaseTest.*; | |||
/** | |||
@@ -53,75 +54,81 @@ public class ImportTest extends TestCase | |||
public void testImportFromFile() throws Exception | |||
{ | |||
Database db = create(); | |||
db.importFile("test", new File("test/data/sample-input.tab"), "\\t"); | |||
for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) { | |||
Database db = create(fileFormat); | |||
db.importFile("test", new File("test/data/sample-input.tab"), "\\t"); | |||
} | |||
} | |||
public void testImportFromFileWithOnlyHeaders() throws Exception | |||
{ | |||
Database db = create(); | |||
db.importFile("test", new File("test/data/sample-input-only-headers.tab"), | |||
for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) { | |||
Database db = create(fileFormat); | |||
db.importFile("test", new File("test/data/sample-input-only-headers.tab"), | |||
"\\t"); | |||
} | |||
} | |||
public void testCopySqlHeaders() throws Exception | |||
{ | |||
TestResultSet rs = new TestResultSet(); | |||
rs.addColumn(Types.INTEGER, "col1"); | |||
rs.addColumn(Types.VARCHAR, "col2", 60, 0, 0); | |||
rs.addColumn(Types.VARCHAR, "col3", 500, 0, 0); | |||
rs.addColumn(Types.BINARY, "col4", 128, 0, 0); | |||
rs.addColumn(Types.BINARY, "col5", 512, 0, 0); | |||
rs.addColumn(Types.NUMERIC, "col6", 0, 7, 15); | |||
rs.addColumn(Types.VARCHAR, "col7", Integer.MAX_VALUE, 0, 0); | |||
Database db = create(); | |||
db.copyTable("Test1", (ResultSet)Proxy.newProxyInstance( | |||
Thread.currentThread().getContextClassLoader(), | |||
new Class[]{ResultSet.class}, | |||
rs)); | |||
Table t = db.getTable("Test1"); | |||
List<Column> columns = t.getColumns(); | |||
assertEquals(7, columns.size()); | |||
Column c = columns.get(0); | |||
assertEquals("col1", c.getName()); | |||
assertEquals(DataType.LONG, c.getType()); | |||
c = columns.get(1); | |||
assertEquals("col2", c.getName()); | |||
assertEquals(DataType.TEXT, c.getType()); | |||
assertEquals(120, c.getLength()); | |||
c = columns.get(2); | |||
assertEquals("col3", c.getName()); | |||
assertEquals(DataType.MEMO, c.getType()); | |||
assertEquals(0, c.getLength()); | |||
c = columns.get(3); | |||
assertEquals("col4", c.getName()); | |||
assertEquals(DataType.BINARY, c.getType()); | |||
assertEquals(128, c.getLength()); | |||
c = columns.get(4); | |||
assertEquals("col5", c.getName()); | |||
assertEquals(DataType.OLE, c.getType()); | |||
assertEquals(0, c.getLength()); | |||
c = columns.get(5); | |||
assertEquals("col6", c.getName()); | |||
assertEquals(DataType.NUMERIC, c.getType()); | |||
assertEquals(17, c.getLength()); | |||
assertEquals(7, c.getScale()); | |||
assertEquals(15, c.getPrecision()); | |||
c = columns.get(6); | |||
assertEquals("col7", c.getName()); | |||
assertEquals(DataType.MEMO, c.getType()); | |||
assertEquals(0, c.getLength()); | |||
for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) { | |||
TestResultSet rs = new TestResultSet(); | |||
rs.addColumn(Types.INTEGER, "col1"); | |||
rs.addColumn(Types.VARCHAR, "col2", 60, 0, 0); | |||
rs.addColumn(Types.VARCHAR, "col3", 500, 0, 0); | |||
rs.addColumn(Types.BINARY, "col4", 128, 0, 0); | |||
rs.addColumn(Types.BINARY, "col5", 512, 0, 0); | |||
rs.addColumn(Types.NUMERIC, "col6", 0, 7, 15); | |||
rs.addColumn(Types.VARCHAR, "col7", Integer.MAX_VALUE, 0, 0); | |||
Database db = create(fileFormat); | |||
db.copyTable("Test1", (ResultSet)Proxy.newProxyInstance( | |||
Thread.currentThread().getContextClassLoader(), | |||
new Class[]{ResultSet.class}, | |||
rs)); | |||
Table t = db.getTable("Test1"); | |||
List<Column> columns = t.getColumns(); | |||
assertEquals(7, columns.size()); | |||
Column c = columns.get(0); | |||
assertEquals("col1", c.getName()); | |||
assertEquals(DataType.LONG, c.getType()); | |||
c = columns.get(1); | |||
assertEquals("col2", c.getName()); | |||
assertEquals(DataType.TEXT, c.getType()); | |||
assertEquals(120, c.getLength()); | |||
c = columns.get(2); | |||
assertEquals("col3", c.getName()); | |||
assertEquals(DataType.MEMO, c.getType()); | |||
assertEquals(0, c.getLength()); | |||
c = columns.get(3); | |||
assertEquals("col4", c.getName()); | |||
assertEquals(DataType.BINARY, c.getType()); | |||
assertEquals(128, c.getLength()); | |||
c = columns.get(4); | |||
assertEquals("col5", c.getName()); | |||
assertEquals(DataType.OLE, c.getType()); | |||
assertEquals(0, c.getLength()); | |||
c = columns.get(5); | |||
assertEquals("col6", c.getName()); | |||
assertEquals(DataType.NUMERIC, c.getType()); | |||
assertEquals(17, c.getLength()); | |||
assertEquals(7, c.getScale()); | |||
assertEquals(15, c.getPrecision()); | |||
c = columns.get(6); | |||
assertEquals("col7", c.getName()); | |||
assertEquals(DataType.MEMO, c.getType()); | |||
assertEquals(0, c.getLength()); | |||
} | |||
} | |||
@@ -27,7 +27,6 @@ King of Prussia, PA 19406 | |||
package com.healthmarketscience.jackcess; | |||
import java.io.File; | |||
import java.lang.reflect.Field; | |||
import java.nio.ByteBuffer; | |||
import java.util.Arrays; | |||
@@ -40,6 +39,7 @@ import java.util.regex.Pattern; | |||
import junit.framework.TestCase; | |||
import static com.healthmarketscience.jackcess.DatabaseTest.*; | |||
import static com.healthmarketscience.jackcess.JetFormatTest.*; | |||
/** | |||
@@ -66,19 +66,21 @@ public class IndexCodesTest extends TestCase { | |||
public void testIndexCodes() throws Exception | |||
{ | |||
Database db = open(new File("test/data/testIndexCodes.mdb")); | |||
for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.INDEX_CODES)) { | |||
Database db = open(testDB); | |||
for(Table t : db) { | |||
for(Index index : t.getIndexes()) { | |||
// System.out.println("Checking " + t.getName() + "." + index.getName()); | |||
checkIndexEntries(t, index); | |||
for(Table t : db) { | |||
for(Index index : t.getIndexes()) { | |||
// System.out.println("Checking " + t.getName() + "." + index.getName()); | |||
checkIndexEntries(testDB, t, index); | |||
} | |||
} | |||
db.close(); | |||
} | |||
db.close(); | |||
} | |||
private static void checkIndexEntries(Table t, Index index) throws Exception | |||
private static void checkIndexEntries(final TestDB testDB, Table t, Index index) throws Exception | |||
{ | |||
// index.initialize(); | |||
// System.out.println("Ind " + index); | |||
@@ -90,7 +92,7 @@ public class IndexCodesTest extends TestCase { | |||
Cursor.Position curPos = cursor.getSavepoint().getCurrentPosition(); | |||
boolean success = false; | |||
try { | |||
findRow(t, index, row, curPos); | |||
findRow(testDB, t, index, row, curPos); | |||
success = true; | |||
} finally { | |||
if(!success) { | |||
@@ -103,7 +105,7 @@ public class IndexCodesTest extends TestCase { | |||
} | |||
private static void findRow(Table t, Index index, | |||
private static void findRow(final TestDB testDB, Table t, Index index, | |||
Map<String,Object> expectedRow, | |||
Cursor.Position expectedPos) | |||
throws Exception | |||
@@ -123,7 +125,7 @@ public class IndexCodesTest extends TestCase { | |||
return; | |||
} | |||
} | |||
fail("Could not find expected row " + expectedRow + " starting at " + | |||
fail("testDB: " + testDB + ";\nCould not find expected row " + expectedRow + " starting at " + | |||
entryToString(startPos)); | |||
} | |||
@@ -140,120 +142,134 @@ public class IndexCodesTest extends TestCase { | |||
public void x_testCreateIsoFile() throws Exception | |||
{ | |||
Database db = create(true); | |||
for (final Database.FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) { | |||
Database db = create(fileFormat, true); | |||
Table t = new TableBuilder("test") | |||
.addColumn(new ColumnBuilder("row", DataType.TEXT).toColumn()) | |||
.addColumn(new ColumnBuilder("data", DataType.TEXT).toColumn()) | |||
.toTable(db); | |||
for(int i = 0; i < 256; ++i) { | |||
String str = "AA" + ((char)i) + "AA"; | |||
t.addRow("row" + i, str); | |||
} | |||
Table t = new TableBuilder("test") | |||
.addColumn(new ColumnBuilder("row", DataType.TEXT).toColumn()) | |||
.addColumn(new ColumnBuilder("data", DataType.TEXT).toColumn()) | |||
.toTable(db); | |||
for(int i = 0; i < 256; ++i) { | |||
String str = "AA" + ((char)i) + "AA"; | |||
t.addRow("row" + i, str); | |||
} | |||
db.close(); | |||
db.close(); | |||
} | |||
} | |||
public void x_testCreateAltIsoFile() throws Exception | |||
{ | |||
Database db = openCopy(new File("/tmp/test_ind.mdb"), true); | |||
Table t = db.getTable("Table1"); | |||
for (final TestDB testDB : TestDB.getSupportedForBasename(null)) { | |||
// @todo Bank test dbFiles and create new TestDB here. | |||
//Database db = openCopy(new File("/tmp/test_ind.mdb"), true); | |||
Database db = openCopy(testDB, true); | |||
Table t = db.getTable("Table1"); | |||
for(int i = 0; i < 256; ++i) { | |||
String str = "AA" + ((char)i) + "AA"; | |||
t.addRow("row" + i, str, | |||
(byte)42 + i, (short)53 + i, 13 * i, | |||
(6.7d / i), null, null, true); | |||
} | |||
for(int i = 0; i < 256; ++i) { | |||
String str = "AA" + ((char)i) + "AA"; | |||
t.addRow("row" + i, str, | |||
(byte)42 + i, (short)53 + i, 13 * i, | |||
(6.7d / i), null, null, true); | |||
db.close(); | |||
} | |||
db.close(); | |||
} | |||
public void x_testWriteAllCodesMdb() throws Exception | |||
{ | |||
Database db = create(true); | |||
// Table t = new TableBuilder("Table1") | |||
// .addColumn(new ColumnBuilder("key", DataType.TEXT).toColumn()) | |||
// .addColumn(new ColumnBuilder("data", DataType.TEXT).toColumn()) | |||
// .toTable(db); | |||
// for(int i = 0; i <= 0xFFFF; ++i) { | |||
// // skip non-char chars | |||
// char c = (char)i; | |||
// if(Character.isHighSurrogate(c) || Character.isLowSurrogate(c)) { | |||
// continue; | |||
// } | |||
// String key = toUnicodeStr(c); | |||
// String str = "AA" + c + "AA"; | |||
// t.addRow(key, str); | |||
// } | |||
Table t = new TableBuilder("Table5") | |||
.addColumn(new ColumnBuilder("name", DataType.TEXT).toColumn()) | |||
.addColumn(new ColumnBuilder("data", DataType.TEXT).toColumn()) | |||
.toTable(db); | |||
char c = (char)0x3041; // crazy 7F 02 ... A0 | |||
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 c6 = (char)0x30F6; // crazy 7F 0D ... A0 | |||
char c7 = (char)0x3099; // unprint 03 | |||
char c8 = (char)0x0041; // A | |||
char c9 = (char)0x002D; // - (unprint) | |||
char c10 = (char)0x20E1; // unprint F2 | |||
char c11 = (char)0x309A; // unprint 04 | |||
char c12 = (char)0x01C4; // (long extra) | |||
char c13 = (char)0x005F; // _ (long inline) | |||
char c14 = (char)0xFFFE; // removed | |||
char[] cs = new char[]{c7, c8, c3, c12, c13, c14, c, c2, c9}; | |||
addCombos(t, 0, "", cs, 5); | |||
// t = new TableBuilder("Table2") | |||
// .addColumn(new ColumnBuilder("data", DataType.TEXT).toColumn()) | |||
// .toTable(db); | |||
// writeChars(0x0000, t); | |||
// t = new TableBuilder("Table3") | |||
// .addColumn(new ColumnBuilder("data", DataType.TEXT).toColumn()) | |||
// .toTable(db); | |||
// writeChars(0x0400, t); | |||
db.close(); | |||
for (final Database.FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) { | |||
Database db = create(fileFormat, true); | |||
// Table t = new TableBuilder("Table1") | |||
// .addColumn(new ColumnBuilder("key", DataType.TEXT).toColumn()) | |||
// .addColumn(new ColumnBuilder("data", DataType.TEXT).toColumn()) | |||
// .toTable(db); | |||
// for(int i = 0; i <= 0xFFFF; ++i) { | |||
// // skip non-char chars | |||
// char c = (char)i; | |||
// if(Character.isHighSurrogate(c) || Character.isLowSurrogate(c)) { | |||
// continue; | |||
// } | |||
// String key = toUnicodeStr(c); | |||
// String str = "AA" + c + "AA"; | |||
// t.addRow(key, str); | |||
// } | |||
Table t = new TableBuilder("Table5") | |||
.addColumn(new ColumnBuilder("name", DataType.TEXT).toColumn()) | |||
.addColumn(new ColumnBuilder("data", DataType.TEXT).toColumn()) | |||
.toTable(db); | |||
char c = (char)0x3041; // crazy 7F 02 ... A0 | |||
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 c6 = (char)0x30F6; // crazy 7F 0D ... A0 | |||
char c7 = (char)0x3099; // unprint 03 | |||
char c8 = (char)0x0041; // A | |||
char c9 = (char)0x002D; // - (unprint) | |||
char c10 = (char)0x20E1; // unprint F2 | |||
char c11 = (char)0x309A; // unprint 04 | |||
char c12 = (char)0x01C4; // (long extra) | |||
char c13 = (char)0x005F; // _ (long inline) | |||
char c14 = (char)0xFFFE; // removed | |||
char[] cs = new char[]{c7, c8, c3, c12, c13, c14, c, c2, c9}; | |||
addCombos(t, 0, "", cs, 5); | |||
// t = new TableBuilder("Table2") | |||
// .addColumn(new ColumnBuilder("data", DataType.TEXT).toColumn()) | |||
// .toTable(db); | |||
// writeChars(0x0000, t); | |||
// t = new TableBuilder("Table3") | |||
// .addColumn(new ColumnBuilder("data", DataType.TEXT).toColumn()) | |||
// .toTable(db); | |||
// writeChars(0x0400, t); | |||
db.close(); | |||
} | |||
} | |||
public void x_testReadAllCodesMdb() throws Exception | |||
{ | |||
// Database db = openCopy(new File("/data2/jackcess_test/testAllIndexCodes.mdb")); | |||
// Database db = openCopy(new File("/data2/jackcess_test/testAllIndexCodes_orig.mdb")); | |||
// Database db = openCopy(new File("/data2/jackcess_test/testSomeMoreCodes.mdb")); | |||
Database db = openCopy(new File("/data2/jackcess_test/testStillMoreCodes.mdb")); | |||
Table t = db.getTable("Table5"); | |||
Index ind = t.getIndexes().iterator().next(); | |||
ind.initialize(); | |||
System.out.println("Ind " + ind); | |||
for (final TestDB testDB : TestDB.getSupportedForBasename(null)) { | |||
// Database db = openCopy(new File("/data2/jackcess_test/testAllIndexCodes.mdb")); | |||
// Database db = openCopy(new File("/data2/jackcess_test/testAllIndexCodes_orig.mdb")); | |||
// Database db = openCopy(new File("/data2/jackcess_test/testSomeMoreCodes.mdb")); | |||
// @todo Bank test dbFiles and create new TestDB here. | |||
//Database db = openCopy(new File("/data2/jackcess_test/testStillMoreCodes.mdb")); | |||
Database db = openCopy(testDB); | |||
Table t = db.getTable("Table5"); | |||
Index ind = t.getIndexes().iterator().next(); | |||
ind.initialize(); | |||
System.out.println("Ind " + ind); | |||
Cursor cursor = Cursor.createIndexCursor(t, ind); | |||
while(cursor.moveToNextRow()) { | |||
System.out.println("======="); | |||
String entryStr = | |||
entryToString(cursor.getSavepoint().getCurrentPosition()); | |||
System.out.println("Entry Bytes: " + entryStr); | |||
System.out.println("Value: " + cursor.getCurrentRow() + "; " + | |||
toUnicodeStr(cursor.getCurrentRow().get("data"))); | |||
} | |||
Cursor cursor = Cursor.createIndexCursor(t, ind); | |||
while(cursor.moveToNextRow()) { | |||
System.out.println("======="); | |||
String entryStr = | |||
entryToString(cursor.getSavepoint().getCurrentPosition()); | |||
System.out.println("Entry Bytes: " + entryStr); | |||
System.out.println("Value: " + cursor.getCurrentRow() + "; " + | |||
toUnicodeStr(cursor.getCurrentRow().get("data"))); | |||
db.close(); | |||
} | |||
db.close(); | |||
} | |||
private int addCombos(Table t, int rowNum, String s, char[] cs, int len) | |||
@@ -285,201 +301,209 @@ public class IndexCodesTest extends TestCase { | |||
public void x_testReadIsoMdb() throws Exception | |||
{ | |||
// Database db = open(new File("/tmp/test_ind.mdb")); | |||
// Database db = open(new File("/tmp/test_ind2.mdb")); | |||
Database db = open(new File("/tmp/test_ind3.mdb")); | |||
// Database db = open(new File("/tmp/test_ind4.mdb")); | |||
Table t = db.getTable("Table1"); | |||
Index index = t.getIndex("B"); | |||
index.initialize(); | |||
System.out.println("Ind " + index); | |||
for (final TestDB testDB : TestDB.getSupportedForBasename(null)) { | |||
// Database db = open(new File("/tmp/test_ind.mdb")); | |||
// Database db = open(new File("/tmp/test_ind2.mdb")); | |||
// @todo Bank test dbFiles and create new TestDB here. | |||
//Database db = open(new File("/tmp/test_ind3.mdb")); | |||
Database db = open(testDB); | |||
// Database db = open(new File("/tmp/test_ind4.mdb")); | |||
Table t = db.getTable("Table1"); | |||
Index index = t.getIndex("B"); | |||
index.initialize(); | |||
System.out.println("Ind " + index); | |||
Cursor cursor = Cursor.createIndexCursor(t, index); | |||
while(cursor.moveToNextRow()) { | |||
System.out.println("======="); | |||
System.out.println("Savepoint: " + cursor.getSavepoint()); | |||
System.out.println("Value: " + cursor.getCurrentRow()); | |||
} | |||
Cursor cursor = Cursor.createIndexCursor(t, index); | |||
while(cursor.moveToNextRow()) { | |||
System.out.println("======="); | |||
System.out.println("Savepoint: " + cursor.getSavepoint()); | |||
System.out.println("Value: " + cursor.getCurrentRow()); | |||
db.close(); | |||
} | |||
db.close(); | |||
} | |||
public void x_testReverseIsoMdb() throws Exception | |||
{ | |||
Database db = open(new File("/data2/jackcess_test/testAllIndexCodes3.mdb")); | |||
Table t = db.getTable("Table1"); | |||
Index index = t.getIndexes().iterator().next(); | |||
index.initialize(); | |||
System.out.println("Ind " + index); | |||
Pattern inlinePat = Pattern.compile("7F 4A 4A (.*)4A 4A 01 00"); | |||
Pattern unprintPat = Pattern.compile("01 01 01 80 (.+) 06 (.+) 00"); | |||
Pattern unprint2Pat = Pattern.compile("4A 4A 4A 4A 01 02 (.+) 00"); | |||
Pattern inatPat = Pattern.compile("7F 4A 4A (.*)4A 4A 01 02 02 (.+) 00"); | |||
Pattern inat2Pat = Pattern.compile("7F 4A 4A (.*)4A 4A 01 (02 02 (.+))?01 01 (.*)FF 02 80 FF 80 00"); | |||
Map<Character,String[]> inlineCodes = new TreeMap<Character,String[]>(); | |||
Map<Character,String[]> unprintCodes = new TreeMap<Character,String[]>(); | |||
Map<Character,String[]> unprint2Codes = new TreeMap<Character,String[]>(); | |||
Map<Character,String[]> inatInlineCodes = new TreeMap<Character,String[]>(); | |||
Map<Character,String[]> inatExtraCodes = new TreeMap<Character,String[]>(); | |||
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 = Cursor.createIndexCursor(t, index); | |||
while(cursor.moveToNextRow()) { | |||
// System.out.println("======="); | |||
// System.out.println("Savepoint: " + cursor.getSavepoint()); | |||
// System.out.println("Value: " + cursor.getCurrentRow()); | |||
Cursor.Savepoint savepoint = cursor.getSavepoint(); | |||
String entryStr = entryToString(savepoint.getCurrentPosition()); | |||
for (final TestDB testDB : TestDB.getSupportedForBasename(null)) { | |||
// @todo Bank test dbFiles and create new TestDB here. | |||
//Database db = open(new File("/data2/jackcess_test/testAllIndexCodes3.mdb")); | |||
Database db = open(testDB); | |||
Table t = db.getTable("Table1"); | |||
Index index = t.getIndexes().iterator().next(); | |||
index.initialize(); | |||
System.out.println("Ind " + index); | |||
Pattern inlinePat = Pattern.compile("7F 4A 4A (.*)4A 4A 01 00"); | |||
Pattern unprintPat = Pattern.compile("01 01 01 80 (.+) 06 (.+) 00"); | |||
Pattern unprint2Pat = Pattern.compile("4A 4A 4A 4A 01 02 (.+) 00"); | |||
Pattern inatPat = Pattern.compile("7F 4A 4A (.*)4A 4A 01 02 02 (.+) 00"); | |||
Pattern inat2Pat = Pattern.compile("7F 4A 4A (.*)4A 4A 01 (02 02 (.+))?01 01 (.*)FF 02 80 FF 80 00"); | |||
Map<Character,String[]> inlineCodes = new TreeMap<Character,String[]>(); | |||
Map<Character,String[]> unprintCodes = new TreeMap<Character,String[]>(); | |||
Map<Character,String[]> unprint2Codes = new TreeMap<Character,String[]>(); | |||
Map<Character,String[]> inatInlineCodes = new TreeMap<Character,String[]>(); | |||
Map<Character,String[]> inatExtraCodes = new TreeMap<Character,String[]>(); | |||
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 = Cursor.createIndexCursor(t, index); | |||
while(cursor.moveToNextRow()) { | |||
// System.out.println("======="); | |||
// System.out.println("Savepoint: " + cursor.getSavepoint()); | |||
// System.out.println("Value: " + cursor.getCurrentRow()); | |||
Cursor.Savepoint savepoint = cursor.getSavepoint(); | |||
String entryStr = entryToString(savepoint.getCurrentPosition()); | |||
Map<String,Object> row = cursor.getCurrentRow(); | |||
String value = (String)row.get("data"); | |||
String key = (String)row.get("key"); | |||
char c = value.charAt(2); | |||
System.out.println("======="); | |||
System.out.println("RowId: " + | |||
savepoint.getCurrentPosition().getRowId()); | |||
System.out.println("Entry: " + entryStr); | |||
// System.out.println("Row: " + row); | |||
System.out.println("Value: (" + key + ")" + value); | |||
System.out.println("Char: " + c + ", " + (int)c + ", " + | |||
toUnicodeStr(c)); | |||
String type = null; | |||
if(entryStr.endsWith("01 00")) { | |||
// handle inline codes | |||
type = "INLINE"; | |||
Matcher m = inlinePat.matcher(entryStr); | |||
m.find(); | |||
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") && | |||
!entryStr.contains("FF 02 80 FF 80")) { | |||
// handle chars w/ symbols | |||
type = "CHAR_WITH_SYMBOL"; | |||
Matcher m = inatPat.matcher(entryStr); | |||
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 | |||
type = "UNPRINTABLE_2"; | |||
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"; | |||
Matcher m = inat2Pat.matcher(entryStr); | |||
m.find(); | |||
handleInternational2Entry(m.group(1), m.group(3), m.group(4), c, | |||
inat2Codes, inat2ExtraCodes, | |||
inat2CrazyCodes); | |||
Map<String,Object> row = cursor.getCurrentRow(); | |||
String value = (String)row.get("data"); | |||
String key = (String)row.get("key"); | |||
char c = value.charAt(2); | |||
System.out.println("======="); | |||
System.out.println("RowId: " + | |||
savepoint.getCurrentPosition().getRowId()); | |||
System.out.println("Entry: " + entryStr); | |||
// System.out.println("Row: " + row); | |||
System.out.println("Value: (" + key + ")" + value); | |||
System.out.println("Char: " + c + ", " + (int)c + ", " + | |||
toUnicodeStr(c)); | |||
String type = null; | |||
if(entryStr.endsWith("01 00")) { | |||
// handle inline codes | |||
type = "INLINE"; | |||
Matcher m = inlinePat.matcher(entryStr); | |||
m.find(); | |||
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") && | |||
!entryStr.contains("FF 02 80 FF 80")) { | |||
// handle chars w/ symbols | |||
type = "CHAR_WITH_SYMBOL"; | |||
Matcher m = inatPat.matcher(entryStr); | |||
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 | |||
type = "UNPRINTABLE_2"; | |||
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"; | |||
Matcher m = inat2Pat.matcher(entryStr); | |||
m.find(); | |||
handleInternational2Entry(m.group(1), m.group(3), m.group(4), c, | |||
inat2Codes, inat2ExtraCodes, | |||
inat2CrazyCodes); | |||
} else { | |||
throw new RuntimeException("unhandled " + entryStr); | |||
} | |||
System.out.println("Type: " + type); | |||
} | |||
} else { | |||
System.out.println("\n***CODES"); | |||
for(int i = 0; i <= 0xFFFF; ++i) { | |||
throw new RuntimeException("unhandled " + entryStr); | |||
} | |||
if(i == 256) { | |||
System.out.println("\n***EXTENDED CODES"); | |||
System.out.println("Type: " + type); | |||
} | |||
// skip non-char chars | |||
char c = (char)i; | |||
if(Character.isHighSurrogate(c) || Character.isLowSurrogate(c)) { | |||
continue; | |||
} | |||
System.out.println("\n***CODES"); | |||
for(int i = 0; i <= 0xFFFF; ++i) { | |||
if(c == (char)0xFFFE) { | |||
// this gets replaced with FFFD, treat it the same | |||
c = (char)0xFFFD; | |||
} | |||
if(i == 256) { | |||
System.out.println("\n***EXTENDED CODES"); | |||
} | |||
Character cc = c; | |||
String[] chars = inlineCodes.get(cc); | |||
if(chars != null) { | |||
if((chars.length == 1) && (chars[0].length() == 0)) { | |||
System.out.println("X"); | |||
} else { | |||
System.out.println("S" + toByteString(chars)); | |||
// skip non-char chars | |||
char c = (char)i; | |||
if(Character.isHighSurrogate(c) || Character.isLowSurrogate(c)) { | |||
continue; | |||
} | |||
continue; | |||
} | |||
chars = inatInlineCodes.get(cc); | |||
if(chars != null) { | |||
String[] extra = inatExtraCodes.get(cc); | |||
System.out.println("I" + toByteString(chars) + "," + | |||
toByteString(extra)); | |||
continue; | |||
} | |||
chars = unprintCodes.get(cc); | |||
if(chars != null) { | |||
System.out.println("U" + toByteString(chars)); | |||
continue; | |||
} | |||
if(c == (char)0xFFFE) { | |||
// this gets replaced with FFFD, treat it the same | |||
c = (char)0xFFFD; | |||
} | |||
chars = unprint2Codes.get(cc); | |||
if(chars != null) { | |||
if(chars.length > 1) { | |||
throw new RuntimeException("long unprint codes"); | |||
Character cc = c; | |||
String[] chars = inlineCodes.get(cc); | |||
if(chars != null) { | |||
if((chars.length == 1) && (chars[0].length() == 0)) { | |||
System.out.println("X"); | |||
} else { | |||
System.out.println("S" + toByteString(chars)); | |||
} | |||
continue; | |||
} | |||
chars = inatInlineCodes.get(cc); | |||
if(chars != null) { | |||
String[] extra = inatExtraCodes.get(cc); | |||
System.out.println("I" + toByteString(chars) + "," + | |||
toByteString(extra)); | |||
continue; | |||
} | |||
chars = unprintCodes.get(cc); | |||
if(chars != null) { | |||
System.out.println("U" + toByteString(chars)); | |||
continue; | |||
} | |||
int val = Integer.parseInt(chars[0], 16) - 2; | |||
String valStr = ByteUtil.toHexString(new byte[]{(byte)val}).trim(); | |||
System.out.println("P" + valStr); | |||
continue; | |||
} | |||
chars = inat2Codes.get(cc); | |||
if(chars != null) { | |||
String [] crazyCodes = inat2CrazyCodes.get(cc); | |||
String crazyCode = ""; | |||
if(crazyCodes != null) { | |||
if((crazyCodes.length != 1) || !"A0".equals(crazyCodes[0])) { | |||
throw new RuntimeException("CC " + Arrays.asList(crazyCodes)); | |||
chars = unprint2Codes.get(cc); | |||
if(chars != null) { | |||
if(chars.length > 1) { | |||
throw new RuntimeException("long unprint codes"); | |||
} | |||
crazyCode = "1"; | |||
int val = Integer.parseInt(chars[0], 16) - 2; | |||
String valStr = ByteUtil.toHexString(new byte[]{(byte)val}).trim(); | |||
System.out.println("P" + valStr); | |||
continue; | |||
} | |||
String[] extra = inat2ExtraCodes.get(cc); | |||
System.out.println("Z" + toByteString(chars) + "," + | |||
toByteString(extra) + "," + | |||
crazyCode); | |||
continue; | |||
chars = inat2Codes.get(cc); | |||
if(chars != null) { | |||
String [] crazyCodes = inat2CrazyCodes.get(cc); | |||
String crazyCode = ""; | |||
if(crazyCodes != null) { | |||
if((crazyCodes.length != 1) || !"A0".equals(crazyCodes[0])) { | |||
throw new RuntimeException("CC " + Arrays.asList(crazyCodes)); | |||
} | |||
crazyCode = "1"; | |||
} | |||
String[] extra = inat2ExtraCodes.get(cc); | |||
System.out.println("Z" + toByteString(chars) + "," + | |||
toByteString(extra) + "," + | |||
crazyCode); | |||
continue; | |||
} | |||
throw new RuntimeException("Unhandled char " + toUnicodeStr(c)); | |||
} | |||
System.out.println("\n***END CODES"); | |||
throw new RuntimeException("Unhandled char " + toUnicodeStr(c)); | |||
db.close(); | |||
} | |||
System.out.println("\n***END CODES"); | |||
db.close(); | |||
} | |||
private static String toByteString(String[] chars) |
@@ -27,7 +27,6 @@ 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; | |||
@@ -40,6 +39,7 @@ import java.util.TreeSet; | |||
import junit.framework.TestCase; | |||
import static com.healthmarketscience.jackcess.DatabaseTest.*; | |||
import static com.healthmarketscience.jackcess.JetFormatTest.*; | |||
/** | |||
* @author James Ahlborn | |||
@@ -84,131 +84,140 @@ public class IndexTest extends TestCase { | |||
} | |||
public void testPrimaryKey() throws Exception { | |||
Table table = open().getTable("Table1"); | |||
Map<String, Boolean> foundPKs = new HashMap<String, Boolean>(); | |||
for(Index index : table.getIndexes()) { | |||
foundPKs.put(index.getColumns().iterator().next().getName(), | |||
index.isPrimaryKey()); | |||
for (final TestDB testDB : SUPPORTED_DBS_TEST) { | |||
Table table = open(testDB).getTable("Table1"); | |||
Map<String, Boolean> foundPKs = new HashMap<String, Boolean>(); | |||
for(Index index : table.getIndexes()) { | |||
foundPKs.put(index.getColumns().iterator().next().getName(), | |||
index.isPrimaryKey()); | |||
} | |||
Map<String, Boolean> expectedPKs = new HashMap<String, Boolean>(); | |||
expectedPKs.put("A", Boolean.TRUE); | |||
expectedPKs.put("B", Boolean.FALSE); | |||
assertEquals(expectedPKs, foundPKs); | |||
} | |||
Map<String, Boolean> expectedPKs = new HashMap<String, Boolean>(); | |||
expectedPKs.put("A", Boolean.TRUE); | |||
expectedPKs.put("B", Boolean.FALSE); | |||
assertEquals(expectedPKs, foundPKs); | |||
} | |||
public void testIndexSlots() throws Exception | |||
{ | |||
Database mdb = open(new File("test/data/indexTest.mdb")); | |||
Table table = mdb.getTable("Table1"); | |||
for(Index idx : table.getIndexes()) { | |||
idx.initialize(); | |||
} | |||
assertEquals(4, table.getIndexes().size()); | |||
assertEquals(4, table.getIndexSlotCount()); | |||
checkIndexColumns(table, | |||
"id", "id", | |||
"PrimaryKey", "id", | |||
"Table2Table1", "otherfk1", | |||
"Table3Table1", "otherfk2"); | |||
table = mdb.getTable("Table2"); | |||
for(Index idx : table.getIndexes()) { | |||
idx.initialize(); | |||
} | |||
assertEquals(2, table.getIndexes().size()); | |||
assertEquals(3, table.getIndexSlotCount()); | |||
checkIndexColumns(table, | |||
"id", "id", | |||
"PrimaryKey", "id"); | |||
for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.INDEX)) { | |||
Database mdb = open(testDB); | |||
table = mdb.getTable("Table3"); | |||
for(Index idx : table.getIndexes()) { | |||
idx.initialize(); | |||
Table table = mdb.getTable("Table1"); | |||
for(Index idx : table.getIndexes()) { | |||
idx.initialize(); | |||
} | |||
assertEquals(4, table.getIndexes().size()); | |||
assertEquals(4, table.getIndexSlotCount()); | |||
checkIndexColumns(table, | |||
"id", "id", | |||
"PrimaryKey", "id", | |||
"Table2Table1", "otherfk1", | |||
"Table3Table1", "otherfk2"); | |||
table = mdb.getTable("Table2"); | |||
for(Index idx : table.getIndexes()) { | |||
idx.initialize(); | |||
} | |||
assertEquals(2, table.getIndexes().size()); | |||
assertEquals(3, table.getIndexSlotCount()); | |||
checkIndexColumns(table, | |||
"id", "id", | |||
"PrimaryKey", "id"); | |||
table = mdb.getTable("Table3"); | |||
for(Index idx : table.getIndexes()) { | |||
idx.initialize(); | |||
} | |||
assertEquals(2, table.getIndexes().size()); | |||
assertEquals(3, table.getIndexSlotCount()); | |||
checkIndexColumns(table, | |||
"id", "id", | |||
"PrimaryKey", "id"); | |||
} | |||
assertEquals(2, table.getIndexes().size()); | |||
assertEquals(3, table.getIndexSlotCount()); | |||
checkIndexColumns(table, | |||
"id", "id", | |||
"PrimaryKey", "id"); | |||
} | |||
public void testComplexIndex() throws Exception | |||
{ | |||
// this file has an index with "compressed" entries and node pages | |||
File origFile = new File("test/data/compIndexTest.mdb"); | |||
Database db = open(origFile); | |||
Table t = db.getTable("Table1"); | |||
Index index = t.getIndexes().get(0); | |||
assertFalse(index.isInitialized()); | |||
assertEquals(512, countRows(t)); | |||
assertEquals(512, index.getEntryCount()); | |||
db.close(); | |||
// copy to temp file and attempt to edit | |||
db = openCopy(origFile); | |||
t = db.getTable("Table1"); | |||
index = t.getIndexes().get(0); | |||
System.out.println("IndexTest: Index type: " + index.getClass()); | |||
try { | |||
t.addRow(99, "abc", "def"); | |||
if(index instanceof SimpleIndex) { | |||
// SimpleIndex doesn't support writing these indexes | |||
fail("Should have thrown UnsupportedOperationException"); | |||
} | |||
} catch(UnsupportedOperationException e) { | |||
// success | |||
if(index instanceof BigIndex) { | |||
throw e; | |||
for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.COMP_INDEX)) { | |||
// this file has an index with "compressed" entries and node pages | |||
Database db = open(testDB); | |||
Table t = db.getTable("Table1"); | |||
Index index = t.getIndexes().get(0); | |||
assertFalse(index.isInitialized()); | |||
assertEquals(512, countRows(t)); | |||
assertEquals(512, index.getEntryCount()); | |||
db.close(); | |||
// copy to temp file and attempt to edit | |||
db = openCopy(testDB); | |||
t = db.getTable("Table1"); | |||
index = t.getIndexes().get(0); | |||
System.out.println("IndexTest: Index type: " + index.getClass()); | |||
try { | |||
t.addRow(99, "abc", "def"); | |||
if(index instanceof SimpleIndex) { | |||
// SimpleIndex doesn't support writing these indexes | |||
fail("Should have thrown UnsupportedOperationException"); | |||
} | |||
} catch(UnsupportedOperationException e) { | |||
// success | |||
if(index instanceof BigIndex) { | |||
throw e; | |||
} | |||
} | |||
} | |||
} | |||
public void testEntryDeletion() throws Exception { | |||
Table table = openCopy(new File("test/data/test.mdb")).getTable("Table1"); | |||
for (final TestDB testDB : SUPPORTED_DBS_TEST) { | |||
Table table = openCopy(testDB).getTable("Table1"); | |||
for(int i = 0; i < 10; ++i) { | |||
table.addRow("foo" + i, "bar" + i, (byte)42 + i, (short)53 + i, 13 * i, | |||
(6.7d / i), null, null, true); | |||
} | |||
table.reset(); | |||
assertRowCount(12, table); | |||
for(int i = 0; i < 10; ++i) { | |||
table.addRow("foo" + i, "bar" + i, (byte)42 + i, (short)53 + i, 13 * i, | |||
(6.7d / i), null, null, true); | |||
} | |||
table.reset(); | |||
assertRowCount(12, table); | |||
for(Index index : table.getIndexes()) { | |||
assertEquals(12, index.getEntryCount()); | |||
} | |||
for(Index index : table.getIndexes()) { | |||
assertEquals(12, index.getEntryCount()); | |||
} | |||
table.reset(); | |||
table.getNextRow(); | |||
table.getNextRow(); | |||
table.deleteCurrentRow(); | |||
table.getNextRow(); | |||
table.deleteCurrentRow(); | |||
table.getNextRow(); | |||
table.getNextRow(); | |||
table.deleteCurrentRow(); | |||
table.getNextRow(); | |||
table.getNextRow(); | |||
table.getNextRow(); | |||
table.deleteCurrentRow(); | |||
table.reset(); | |||
assertRowCount(8, table); | |||
for(Index index : table.getIndexes()) { | |||
assertEquals(8, index.getEntryCount()); | |||
table.reset(); | |||
table.getNextRow(); | |||
table.getNextRow(); | |||
table.deleteCurrentRow(); | |||
table.getNextRow(); | |||
table.deleteCurrentRow(); | |||
table.getNextRow(); | |||
table.getNextRow(); | |||
table.deleteCurrentRow(); | |||
table.getNextRow(); | |||
table.getNextRow(); | |||
table.getNextRow(); | |||
table.deleteCurrentRow(); | |||
table.reset(); | |||
assertRowCount(8, table); | |||
for(Index index : table.getIndexes()) { | |||
assertEquals(8, index.getEntryCount()); | |||
} | |||
} | |||
} | |||
public void testIgnoreNulls() throws Exception | |||
{ | |||
Database db = openCopy(new File("test/data/testIndexProperties.mdb")); | |||
for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.INDEX_PROPERTIES)) { | |||
Database db = openCopy(testDB); | |||
doTestIgnoreNulls(db, "TableIgnoreNulls1"); | |||
doTestIgnoreNulls(db, "TableIgnoreNulls2"); | |||
doTestIgnoreNulls(db, "TableIgnoreNulls1"); | |||
doTestIgnoreNulls(db, "TableIgnoreNulls2"); | |||
db.close(); | |||
db.close(); | |||
} | |||
} | |||
private void doTestIgnoreNulls(Database db, String tableName) | |||
@@ -250,41 +259,43 @@ public class IndexTest extends TestCase { | |||
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(); | |||
for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.INDEX_PROPERTIES)) { | |||
Database db = openCopy(testDB); | |||
Table t = db.getTable("TableUnique1_temp"); | |||
Index index = t.getIndex("DataIndex"); | |||
doTestUnique(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(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, | |||
private void doTestUnique(Index index, int numValues, | |||
Object... testData) | |||
throws Exception | |||
{ | |||
@@ -312,70 +323,79 @@ public class IndexTest extends TestCase { | |||
} | |||
public void testUniqueEntryCount() throws Exception { | |||
Database db = openCopy(new File("test/data/test.mdb")); | |||
Table table = db.getTable("Table1"); | |||
Index indA = table.getIndex("PrimaryKey"); | |||
Index indB = table.getIndex("B"); | |||
for (final TestDB testDB : SUPPORTED_DBS_TEST) { | |||
Database db = openCopy(testDB); | |||
Table table = db.getTable("Table1"); | |||
Index indA = table.getIndex("PrimaryKey"); | |||
Index indB = table.getIndex("B"); | |||
assertEquals(2, indA.getUniqueEntryCount()); | |||
assertEquals(2, indB.getUniqueEntryCount()); | |||
assertEquals(2, indA.getUniqueEntryCount()); | |||
assertEquals(2, indB.getUniqueEntryCount()); | |||
List<String> bElems = Arrays.asList("bar", null, "baz", "argle", null, | |||
"bazzle", "37", "bar", "bar", "BAZ"); | |||
for(int i = 0; i < 10; ++i) { | |||
table.addRow("foo" + i, bElems.get(i), (byte)42 + i, (short)53 + i, | |||
13 * i, (6.7d / i), null, null, true); | |||
} | |||
List<String> bElems = Arrays.asList("bar", null, "baz", "argle", null, | |||
"bazzle", "37", "bar", "bar", "BAZ"); | |||
assertEquals(12, indA.getEntryCount()); | |||
assertEquals(12, indB.getEntryCount()); | |||
assertEquals(12, indA.getUniqueEntryCount()); | |||
assertEquals(8, indB.getUniqueEntryCount()); | |||
for(int i = 0; i < 10; ++i) { | |||
table.addRow("foo" + i, bElems.get(i), (byte)42 + i, (short)53 + i, | |||
13 * i, (6.7d / i), null, null, true); | |||
} | |||
table = null; | |||
indA = null; | |||
indB = null; | |||
assertEquals(12, indA.getEntryCount()); | |||
assertEquals(12, indB.getEntryCount()); | |||
table = db.getTable("Table1"); | |||
indA = table.getIndex("PrimaryKey"); | |||
indB = table.getIndex("B"); | |||
assertEquals(12, indA.getEntryCount()); | |||
assertEquals(12, indB.getEntryCount()); | |||
assertEquals(12, indA.getUniqueEntryCount()); | |||
assertEquals(8, indB.getUniqueEntryCount()); | |||
Cursor c = Cursor.createCursor(table); | |||
assertTrue(c.moveToNextRow()); | |||
Map<String,Object> row = c.getCurrentRow(); | |||
assertEquals("abcdefg", row.get("A")); | |||
assertEquals("hijklmnop", row.get("B")); | |||
c.deleteCurrentRow(); | |||
assertEquals(11, indA.getEntryCount()); | |||
assertEquals(11, indB.getEntryCount()); | |||
assertEquals(12, indA.getUniqueEntryCount()); | |||
assertEquals(8, indB.getUniqueEntryCount()); | |||
db.close(); | |||
assertEquals(12, indA.getUniqueEntryCount()); | |||
assertEquals(8, indB.getUniqueEntryCount()); | |||
table = null; | |||
indA = null; | |||
indB = null; | |||
table = db.getTable("Table1"); | |||
indA = table.getIndex("PrimaryKey"); | |||
indB = table.getIndex("B"); | |||
assertEquals(12, indA.getEntryCount()); | |||
assertEquals(12, indB.getEntryCount()); | |||
assertEquals(12, indA.getUniqueEntryCount()); | |||
assertEquals(8, indB.getUniqueEntryCount()); | |||
Cursor c = Cursor.createCursor(table); | |||
assertTrue(c.moveToNextRow()); | |||
final Map<String,Object> row = c.getCurrentRow(); | |||
// Row order is arbitrary, so v2007 row order difference is valid | |||
if (Database.FileFormat.V2007.equals(testDB.getExpectedFileFormat())) { | |||
DatabaseTest.checkTestDBTable1RowA(testDB, table, row); | |||
} else { | |||
DatabaseTest.checkTestDBTable1RowABCDEFG(testDB, table, row); | |||
} | |||
c.deleteCurrentRow(); | |||
assertEquals(11, indA.getEntryCount()); | |||
assertEquals(11, indB.getEntryCount()); | |||
assertEquals(12, indA.getUniqueEntryCount()); | |||
assertEquals(8, indB.getUniqueEntryCount()); | |||
db.close(); | |||
} | |||
} | |||
public void testReplId() throws Exception | |||
{ | |||
Database db = openCopy(new File("test/data/test.mdb")); | |||
Table table = db.getTable("Table4"); | |||
for (final TestDB testDB : SUPPORTED_DBS_TEST) { | |||
Database db = openCopy(testDB); | |||
Table table = db.getTable("Table4"); | |||
for(int i = 0; i< 20; ++i) { | |||
table.addRow("row" + i, Column.AUTO_NUMBER); | |||
} | |||
for(int i = 0; i< 20; ++i) { | |||
table.addRow("row" + i, Column.AUTO_NUMBER); | |||
} | |||
assertEquals(20, table.getRowCount()); | |||
db.close(); | |||
assertEquals(20, table.getRowCount()); | |||
db.close(); | |||
} | |||
} | |||
private void checkIndexColumns(Table table, String... idxInfo) |
@@ -0,0 +1,134 @@ | |||
package com.healthmarketscience.jackcess; | |||
import junit.framework.TestCase; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.nio.channels.FileChannel; | |||
/** | |||
* @author Dan Rollo | |||
* Date: Mar 5, 2010 | |||
* Time: 12:44:21 PM | |||
*/ | |||
public final class JetFormatTest extends TestCase { | |||
private static final File DIR_TEST_DATA = new File("test/data"); | |||
/** | |||
* Defines known valid db test file base names. | |||
*/ | |||
public static enum Basename { | |||
BIG_INDEX("bigIndexTest"), | |||
COMP_INDEX("compIndexTest"), | |||
DEL_COL("delColTest"), | |||
DEL("delTest"), | |||
FIXED_NUMERIC("fixedNumericTest"), | |||
FIXED_TEXT("fixedTextTest"), | |||
INDEX_CURSOR("indexCursorTest"), | |||
INDEX("indexTest"), | |||
OVERFLOW("overflowTest"), | |||
QUERY("queryTest"), | |||
TEST("test"), | |||
TEST2("test2"), | |||
INDEX_CODES("testIndexCodes"), | |||
INDEX_PROPERTIES("testIndexProperties"), | |||
PROMOTION("testPromotion"), | |||
; | |||
private final String basename; | |||
Basename(final String fileBasename) { | |||
basename = fileBasename; | |||
} | |||
} | |||
/** Defines currently supported db file formats. */ | |||
final static Database.FileFormat[] SUPPORTED_FILEFORMATS = new Database.FileFormat[] { | |||
Database.FileFormat.V2000, | |||
Database.FileFormat.V2003, | |||
Database.FileFormat.V2007, | |||
// @todo Uncomment these elements to run test other formats | |||
}; | |||
/** | |||
* Defines known valid test database files, and their jet format version. | |||
*/ | |||
public static final class TestDB { | |||
private final File dbFile; | |||
private final Database.FileFormat expectedFileFormat; | |||
private TestDB(final File databaseFile, final Database.FileFormat expectedDBFileFormat) { | |||
dbFile = databaseFile; | |||
expectedFileFormat = expectedDBFileFormat; | |||
} | |||
public final File getFile() { return dbFile; } | |||
public final Database.FileFormat getExpectedFileFormat() { return expectedFileFormat; } | |||
public final JetFormat getExpectedFormat() { return expectedFileFormat.getJetFormat(); } | |||
public final String toString() { | |||
return "dbFile: " + dbFile.getAbsolutePath() | |||
+ "; expectedFileFormat: " + expectedFileFormat; | |||
} | |||
public static TestDB[] getSupportedForBasename(final Basename basename) { | |||
final TestDB[] supportedTestDBs = new TestDB[SUPPORTED_FILEFORMATS.length]; | |||
int i = 0; | |||
for (final Database.FileFormat fileFormat: SUPPORTED_FILEFORMATS) { | |||
supportedTestDBs[i++] = new TestDB( | |||
getFileForBasename(basename, fileFormat), | |||
fileFormat); | |||
} | |||
return supportedTestDBs; | |||
} | |||
private static File getFileForBasename(Basename basename, Database.FileFormat fileFormat) { | |||
return new File(DIR_TEST_DATA, | |||
fileFormat.name() + "/" + basename.basename + fileFormat.name() + | |||
fileFormat.getFileExtension()); | |||
} | |||
} | |||
static final TestDB UNSUPPORTED_TEST_V1997 = new TestDB( | |||
TestDB.getFileForBasename(Basename.TEST, Database.FileFormat.V1997), Database.FileFormat.V1997); | |||
static final TestDB[] SUPPORTED_DBS_TEST= TestDB.getSupportedForBasename(Basename.TEST); | |||
public void testGetFormat() throws Exception { | |||
try { | |||
JetFormat.getFormat(null); | |||
fail("npe"); | |||
} catch (NullPointerException e) { | |||
assertNull(e.getMessage()); | |||
} | |||
checkJetFormat(UNSUPPORTED_TEST_V1997); | |||
for (final TestDB testDB : SUPPORTED_DBS_TEST) { | |||
checkJetFormat(testDB); | |||
} | |||
} | |||
private static void checkJetFormat(final TestDB testDB) | |||
throws IOException { | |||
final FileChannel channel = Database.openChannel(testDB.dbFile, false); | |||
try { | |||
final JetFormat fmtActual = JetFormat.getFormat(channel); | |||
assertEquals("Unexpected JetFormat for dbFile: " + testDB.dbFile.getAbsolutePath(), | |||
testDB.expectedFileFormat.getJetFormat(), fmtActual); | |||
} finally { | |||
channel.close(); | |||
} | |||
} | |||
} |
@@ -27,13 +27,13 @@ King of Prussia, PA 19406 | |||
package com.healthmarketscience.jackcess; | |||
import java.io.File; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
import junit.framework.TestCase; | |||
import static com.healthmarketscience.jackcess.DatabaseTest.*; | |||
import static com.healthmarketscience.jackcess.JetFormatTest.*; | |||
/** | |||
* @author James Ahlborn | |||
@@ -45,50 +45,51 @@ public class RelationshipTest extends TestCase { | |||
} | |||
public void testSimple() throws Exception { | |||
Database db = open(new File("test/data/indexTest.mdb")); | |||
Table t1 = db.getTable("Table1"); | |||
Table t2 = db.getTable("Table2"); | |||
Table t3 = db.getTable("Table3"); | |||
List<Relationship> rels = db.getRelationships(t1, t2); | |||
assertEquals(1, rels.size()); | |||
Relationship rel = rels.get(0); | |||
assertEquals("Table2Table1", rel.getName()); | |||
assertEquals(t2, rel.getFromTable()); | |||
assertEquals(Arrays.asList(t2.getColumn("id")), | |||
rel.getFromColumns()); | |||
assertEquals(t1, rel.getToTable()); | |||
assertEquals(Arrays.asList(t1.getColumn("otherfk1")), | |||
rel.getToColumns()); | |||
assertTrue(rel.hasReferentialIntegrity()); | |||
assertEquals(0, rel.getFlags()); | |||
assertSameRelationships(rels, db.getRelationships(t2, t1)); | |||
rels = db.getRelationships(t2, t3); | |||
assertTrue(db.getRelationships(t2, t3).isEmpty()); | |||
assertSameRelationships(rels, db.getRelationships(t3, t2)); | |||
rels = db.getRelationships(t1, t3); | |||
assertEquals(1, rels.size()); | |||
rel = rels.get(0); | |||
assertEquals("Table3Table1", rel.getName()); | |||
assertEquals(t3, rel.getFromTable()); | |||
assertEquals(Arrays.asList(t3.getColumn("id")), | |||
rel.getFromColumns()); | |||
assertEquals(t1, rel.getToTable()); | |||
assertEquals(Arrays.asList(t1.getColumn("otherfk2")), | |||
rel.getToColumns()); | |||
assertTrue(rel.hasReferentialIntegrity()); | |||
assertEquals(0, rel.getFlags()); | |||
assertSameRelationships(rels, db.getRelationships(t3, t1)); | |||
try { | |||
db.getRelationships(t1, t1); | |||
fail("IllegalArgumentException should have been thrown"); | |||
} catch(IllegalArgumentException ignored) { | |||
// success | |||
for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.INDEX)) { | |||
Database db = open(testDB); | |||
Table t1 = db.getTable("Table1"); | |||
Table t2 = db.getTable("Table2"); | |||
Table t3 = db.getTable("Table3"); | |||
List<Relationship> rels = db.getRelationships(t1, t2); | |||
assertEquals(1, rels.size()); | |||
Relationship rel = rels.get(0); | |||
assertEquals("Table2Table1", rel.getName()); | |||
assertEquals(t2, rel.getFromTable()); | |||
assertEquals(Arrays.asList(t2.getColumn("id")), | |||
rel.getFromColumns()); | |||
assertEquals(t1, rel.getToTable()); | |||
assertEquals(Arrays.asList(t1.getColumn("otherfk1")), | |||
rel.getToColumns()); | |||
assertTrue(rel.hasReferentialIntegrity()); | |||
assertEquals(0, rel.getFlags()); | |||
assertSameRelationships(rels, db.getRelationships(t2, t1)); | |||
rels = db.getRelationships(t2, t3); | |||
assertTrue(db.getRelationships(t2, t3).isEmpty()); | |||
assertSameRelationships(rels, db.getRelationships(t3, t2)); | |||
rels = db.getRelationships(t1, t3); | |||
assertEquals(1, rels.size()); | |||
rel = rels.get(0); | |||
assertEquals("Table3Table1", rel.getName()); | |||
assertEquals(t3, rel.getFromTable()); | |||
assertEquals(Arrays.asList(t3.getColumn("id")), | |||
rel.getFromColumns()); | |||
assertEquals(t1, rel.getToTable()); | |||
assertEquals(Arrays.asList(t1.getColumn("otherfk2")), | |||
rel.getToColumns()); | |||
assertTrue(rel.hasReferentialIntegrity()); | |||
assertEquals(0, rel.getFlags()); | |||
assertSameRelationships(rels, db.getRelationships(t3, t1)); | |||
try { | |||
db.getRelationships(t1, t1); | |||
fail("IllegalArgumentException should have been thrown"); | |||
} catch(IllegalArgumentException ignored) { | |||
// success | |||
} | |||
} | |||
} | |||
private void assertSameRelationships( |
@@ -0,0 +1,57 @@ | |||
package com.healthmarketscience.jackcess; | |||
import junit.framework.TestCase; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import static com.healthmarketscience.jackcess.JetFormatTest.*; | |||
/** | |||
* @author Dan Rollo | |||
* Date: Mar 5, 2010 | |||
* Time: 2:21:22 PM | |||
*/ | |||
public final class UsageMapTest extends TestCase { | |||
public void testRead() throws Exception { | |||
try { | |||
Database.open(UNSUPPORTED_TEST_V1997.getFile()); | |||
fail("mdb v97 usage map unsupported"); | |||
} catch (IOException e) { | |||
assertEquals(UsageMap.MSG_PREFIX_UNRECOGNIZED_MAP + 2, e.getMessage()); | |||
} | |||
for (final TestDB testDB : SUPPORTED_DBS_TEST) { | |||
final int expectedFirstPage; | |||
final int expectedLastPage; | |||
final Database.FileFormat expectedFileFormat = testDB.getExpectedFileFormat(); | |||
if (Database.FileFormat.V2000.equals(expectedFileFormat)) { | |||
expectedFirstPage = 743; | |||
expectedLastPage = 767; | |||
} else if (Database.FileFormat.V2003.equals(expectedFileFormat)) { | |||
expectedFirstPage = 16; | |||
expectedLastPage = 799; | |||
} else if (Database.FileFormat.V2007.equals(expectedFileFormat)) { | |||
expectedFirstPage = 42; | |||
expectedLastPage = 511; | |||
} else { | |||
throw new IllegalAccessException("Unknown file format: " + expectedFileFormat); | |||
} | |||
checkUsageMapRead(testDB.getFile(), expectedFirstPage, expectedLastPage); | |||
} | |||
} | |||
private static void checkUsageMapRead(final File dbFile, | |||
final int expectedFirstPage, final int expectedLastPage) | |||
throws IOException { | |||
final Database db = Database.open(dbFile); | |||
final UsageMap usageMap = UsageMap.read(db, | |||
PageChannel.PAGE_GLOBAL_USAGE_MAP, | |||
PageChannel.ROW_GLOBAL_USAGE_MAP, | |||
true); | |||
assertEquals("Unexpected FirstPageNumber.", expectedFirstPage, usageMap.getFirstPageNumber()); | |||
assertEquals("Unexpected LastPageNumber.", expectedLastPage, usageMap.getLastPageNumber()); | |||
} | |||
} |
@@ -27,7 +27,6 @@ King of Prussia, PA 19406 | |||
package com.healthmarketscience.jackcess.query; | |||
import java.io.File; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.HashMap; | |||
@@ -45,6 +44,8 @@ import org.apache.commons.lang.StringUtils; | |||
import static org.apache.commons.lang.SystemUtils.LINE_SEPARATOR; | |||
import static com.healthmarketscience.jackcess.query.QueryFormat.*; | |||
import static com.healthmarketscience.jackcess.JetFormatTest.*; | |||
/** | |||
* @author James Ahlborn | |||
@@ -199,68 +200,70 @@ public class QueryTest extends TestCase | |||
public void testReadQueries() throws Exception | |||
{ | |||
Map<String,String> expectedQueries = new HashMap<String,String>(); | |||
expectedQueries.put( | |||
"SelectQuery", multiline( | |||
"SELECT DISTINCT Table1.*, Table2.col1, Table2.col2, Table3.col3", | |||
"FROM (Table1 LEFT JOIN Table3 ON Table1.col1 = Table3.col1) INNER JOIN Table2 ON (Table3.col1 = Table2.col1) AND (Table3.col1 = Table2.col2)", | |||
"WHERE (((Table2.col2)=\"foo\" Or (Table2.col2) In (\"buzz\",\"bazz\")))", | |||
"ORDER BY Table2.col1;")); | |||
expectedQueries.put( | |||
"DeleteQuery", multiline( | |||
"DELETE Table1.col1, Table1.col2, Table1.col3", | |||
"FROM Table1", | |||
"WHERE (((Table1.col1)>\"blah\"));")); | |||
expectedQueries.put( | |||
"AppendQuery",multiline( | |||
"INSERT INTO Table3", | |||
"SELECT [Table1].[col2], [Table2].[col2], [Table2].[col3]", | |||
"FROM Table3, Table1 INNER JOIN Table2 ON [Table1].[col1]=[Table2].[col1];")); | |||
expectedQueries.put( | |||
"UpdateQuery",multiline( | |||
"PARAMETERS User Name Text;", | |||
"UPDATE Table1", | |||
"SET Table1.col1 = \"foo\", Table1.col2 = [Table2].[col3], [[Table2]].[[col1]] = [User Name]", | |||
"WHERE ((([Table2].[col1]) Is Not Null));")); | |||
expectedQueries.put( | |||
"MakeTableQuery",multiline( | |||
"SELECT Max(Table2.col1) AS MaxOfcol1, Table2.col2, Table3.col2 INTO Table4", | |||
"FROM (Table2 INNER JOIN Table1 ON Table2.col1 = Table1.col2) RIGHT JOIN Table3 ON Table1.col2 = Table3.col3", | |||
"GROUP BY Table2.col2, Table3.col2", | |||
"HAVING (((Max(Table2.col1))=\"buzz\") AND ((Table2.col2)<>\"blah\"));")); | |||
expectedQueries.put( | |||
"CrosstabQuery", multiline( | |||
"TRANSFORM Count([Table2].[col2]) AS CountOfcol2", | |||
"SELECT Table2_1.col1, [Table2].[col3], Avg(Table2_1.col2) AS AvgOfcol2", | |||
"FROM (Table1 INNER JOIN Table2 ON [Table1].[col1]=[Table2].[col1]) INNER JOIN Table2 AS Table2_1 ON [Table2].[col1]=Table2_1.col3", | |||
"WHERE ((([Table1].[col1])>\"10\") And ((Table2_1.col1) Is Not Null) And ((Avg(Table2_1.col2))>\"10\"))", | |||
"GROUP BY Table2_1.col1, [Table2].[col3]", | |||
"ORDER BY [Table2].[col3]", | |||
"PIVOT [Table1].[col1];")); | |||
expectedQueries.put( | |||
"UnionQuery", multiline( | |||
"Select Table1.col1, Table1.col2", | |||
"where Table1.col1 = \"foo\"", | |||
"UNION", | |||
"Select Table2.col1, Table2.col2", | |||
"UNION ALL Select Table3.col1, Table3.col2", | |||
"where Table3.col3 > \"blah\";")); | |||
expectedQueries.put( | |||
"PassthroughQuery", multiline( | |||
"ALTER TABLE Table4 DROP COLUMN col5;\0")); | |||
expectedQueries.put( | |||
"DataDefinitionQuery", multiline( | |||
"CREATE TABLE Table5 (col1 CHAR, col2 CHAR);\0")); | |||
Database db = DatabaseTest.open(new File("test/data/queryTest.mdb")); | |||
for(Query q : db.getQueries()) { | |||
assertEquals(expectedQueries.remove(q.getName()), q.toSQLString()); | |||
} | |||
for (final TestDB testDB : TestDB.getSupportedForBasename(Basename.QUERY)) { | |||
Map<String,String> expectedQueries = new HashMap<String,String>(); | |||
expectedQueries.put( | |||
"SelectQuery", multiline( | |||
"SELECT DISTINCT Table1.*, Table2.col1, Table2.col2, Table3.col3", | |||
"FROM (Table1 LEFT JOIN Table3 ON Table1.col1 = Table3.col1) INNER JOIN Table2 ON (Table3.col1 = Table2.col1) AND (Table3.col1 = Table2.col2)", | |||
"WHERE (((Table2.col2)=\"foo\" Or (Table2.col2) In (\"buzz\",\"bazz\")))", | |||
"ORDER BY Table2.col1;")); | |||
expectedQueries.put( | |||
"DeleteQuery", multiline( | |||
"DELETE Table1.col1, Table1.col2, Table1.col3", | |||
"FROM Table1", | |||
"WHERE (((Table1.col1)>\"blah\"));")); | |||
expectedQueries.put( | |||
"AppendQuery",multiline( | |||
"INSERT INTO Table3", | |||
"SELECT [Table1].[col2], [Table2].[col2], [Table2].[col3]", | |||
"FROM Table3, Table1 INNER JOIN Table2 ON [Table1].[col1]=[Table2].[col1];")); | |||
expectedQueries.put( | |||
"UpdateQuery",multiline( | |||
"PARAMETERS User Name Text;", | |||
"UPDATE Table1", | |||
"SET Table1.col1 = \"foo\", Table1.col2 = [Table2].[col3], [[Table2]].[[col1]] = [User Name]", | |||
"WHERE ((([Table2].[col1]) Is Not Null));")); | |||
expectedQueries.put( | |||
"MakeTableQuery",multiline( | |||
"SELECT Max(Table2.col1) AS MaxOfcol1, Table2.col2, Table3.col2 INTO Table4", | |||
"FROM (Table2 INNER JOIN Table1 ON Table2.col1 = Table1.col2) RIGHT JOIN Table3 ON Table1.col2 = Table3.col3", | |||
"GROUP BY Table2.col2, Table3.col2", | |||
"HAVING (((Max(Table2.col1))=\"buzz\") AND ((Table2.col2)<>\"blah\"));")); | |||
expectedQueries.put( | |||
"CrosstabQuery", multiline( | |||
"TRANSFORM Count([Table2].[col2]) AS CountOfcol2", | |||
"SELECT Table2_1.col1, [Table2].[col3], Avg(Table2_1.col2) AS AvgOfcol2", | |||
"FROM (Table1 INNER JOIN Table2 ON [Table1].[col1]=[Table2].[col1]) INNER JOIN Table2 AS Table2_1 ON [Table2].[col1]=Table2_1.col3", | |||
"WHERE ((([Table1].[col1])>\"10\") And ((Table2_1.col1) Is Not Null) And ((Avg(Table2_1.col2))>\"10\"))", | |||
"GROUP BY Table2_1.col1, [Table2].[col3]", | |||
"ORDER BY [Table2].[col3]", | |||
"PIVOT [Table1].[col1];")); | |||
expectedQueries.put( | |||
"UnionQuery", multiline( | |||
"Select Table1.col1, Table1.col2", | |||
"where Table1.col1 = \"foo\"", | |||
"UNION", | |||
"Select Table2.col1, Table2.col2", | |||
"UNION ALL Select Table3.col1, Table3.col2", | |||
"where Table3.col3 > \"blah\";")); | |||
expectedQueries.put( | |||
"PassthroughQuery", multiline( | |||
"ALTER TABLE Table4 DROP COLUMN col5;\0")); | |||
expectedQueries.put( | |||
"DataDefinitionQuery", multiline( | |||
"CREATE TABLE Table5 (col1 CHAR, col2 CHAR);\0")); | |||
Database db = DatabaseTest.open(testDB); | |||
for(Query q : db.getQueries()) { | |||
assertEquals(expectedQueries.remove(q.getName()), q.toSQLString()); | |||
} | |||
assertTrue(expectedQueries.isEmpty()); | |||
assertTrue(expectedQueries.isEmpty()); | |||
db.close(); | |||
db.close(); | |||
} | |||
} | |||
private void doTestColumns(SelectQuery query) throws Exception |