git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@454 f203690c-595d-4dc9-a70b-905162fa7fd2tags/jackcess-1.2.0
Jon Iles - Added support for reading table definitions that span multiple pages | Jon Iles - Added support for reading table definitions that span multiple pages | ||||
James Schopp - added support for reading currency columns | James Schopp - added support for reading currency columns | ||||
Patricia Donaldson - contributed RowFilter class | Patricia Donaldson - contributed RowFilter class | ||||
Dan Rollo - added support for new DB file formats |
<name>Dan Rollo</name> | <name>Dan Rollo</name> | ||||
<id>bhamail</id> | <id>bhamail</id> | ||||
<email>bhamail@users.sf.net</email> | <email>bhamail@users.sf.net</email> | ||||
<organization>Composite Software, Inc.</organization> | |||||
<timezone>-5</timezone> | <timezone>-5</timezone> | ||||
</developer> | </developer> | ||||
</developers> | </developers> |
<organization>Health Market Science, Inc.</organization> | <organization>Health Market Science, Inc.</organization> | ||||
<timezone>-5</timezone> | <timezone>-5</timezone> | ||||
</developer> | </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> | </developers> | ||||
<licenses> | <licenses> | ||||
<license> | <license> |
} | } | ||||
return true; | 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 | * Convert a byte buffer to a hexadecimal string for display |
private static final String CAT_COL_DATE_UPDATE = "DateUpdate"; | private static final String CAT_COL_DATE_UPDATE = "DateUpdate"; | ||||
/** System catalog column name of the flags column */ | /** System catalog column name of the flags column */ | ||||
private static final String CAT_COL_FLAGS = "Flags"; | 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 */ | /** Prefix for column or table names that are reserved words */ | ||||
private static final String ESCAPE_PREFIX = "x"; | private static final String ESCAPE_PREFIX = "x"; | ||||
/** Prefix that flags system tables */ | /** Prefix that flags system tables */ | ||||
private static final String TABLE_SYSTEM_RELATIONSHIPS = "MSysRelationships"; | private static final String TABLE_SYSTEM_RELATIONSHIPS = "MSysRelationships"; | ||||
/** Name of the table that contains queries */ | /** Name of the table that contains queries */ | ||||
private static final String TABLE_SYSTEM_QUERIES = "MSysQueries"; | 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 */ | /** System object type for table definitions */ | ||||
private static final Short TYPE_TABLE = (short) 1; | private static final Short TYPE_TABLE = (short) 1; | ||||
/** System object type for query definitions */ | /** System object type for query definitions */ | ||||
private boolean _useBigIndex; | private boolean _useBigIndex; | ||||
/** optional error handler to use when row errors are encountered */ | /** optional error handler to use when row errors are encountered */ | ||||
private ErrorHandler _dbErrorHandler; | private ErrorHandler _dbErrorHandler; | ||||
/** the file format of the database */ | |||||
private FileFormat _fileFormat; | |||||
/** | /** | ||||
* Open an existing Database. If the existing file is not writeable, the | * Open an existing Database. If the existing file is not writeable, the | ||||
} | } | ||||
return new Database(openChannel(mdbFile, | return new Database(openChannel(mdbFile, | ||||
(!mdbFile.canWrite() || readOnly)), | (!mdbFile.canWrite() || readOnly)), | ||||
autoSync); | |||||
autoSync, null); | |||||
} | } | ||||
/** | /** | ||||
* Create a new Database | |||||
* Create a new Access 2000 Database | |||||
* <p> | * <p> | ||||
* Equivalent to: | * 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 | * @param mdbFile Location to write the new database to. <b>If this file | ||||
* already exists, it will be overwritten.</b> | * already exists, it will be overwritten.</b> | ||||
} | } | ||||
/** | /** | ||||
* 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 | * @param mdbFile Location to write the new database to. <b>If this file | ||||
* already exists, it will be overwritten.</b> | * already exists, it will be overwritten.</b> | ||||
* @param autoSync whether or not to enable auto-syncing on write. if | * @param autoSync whether or not to enable auto-syncing on write. if | ||||
*/ | */ | ||||
public static Database create(File mdbFile, boolean autoSync) | public static Database create(File mdbFile, boolean autoSync) | ||||
throws IOException | 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); | FileChannel channel = openChannel(mdbFile, false); | ||||
channel.truncate(0); | channel.truncate(0); | ||||
channel.transferFrom(Channels.newChannel( | channel.transferFrom(Channels.newChannel( | ||||
Thread.currentThread().getContextClassLoader().getResourceAsStream( | 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 | throws FileNotFoundException | ||||
{ | { | ||||
String mode = (readOnly ? "r" : "rw"); | |||||
final String mode = (readOnly ? "r" : "rw"); | |||||
return new RandomAccessFile(mdbFile, mode).getChannel(); | return new RandomAccessFile(mdbFile, mode).getChannel(); | ||||
} | } | ||||
* FileChannel instead of a ReadableByteChannel because we need to | * FileChannel instead of a ReadableByteChannel because we need to | ||||
* randomly jump around to various points in the file. | * 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); | _format = JetFormat.getFormat(channel); | ||||
_fileFormat = fileFormat; | |||||
_pageChannel = new PageChannel(channel, _format, autoSync); | _pageChannel = new PageChannel(channel, _format, autoSync); | ||||
// note, it's slighly sketchy to pass ourselves along partially | // note, it's slighly sketchy to pass ourselves along partially | ||||
// constructed, but only our _format and _pageChannel refs should be | // constructed, but only our _format and _pageChannel refs should be | ||||
public void setErrorHandler(ErrorHandler newErrorHandler) { | public void setErrorHandler(ErrorHandler newErrorHandler) { | ||||
_dbErrorHandler = 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 | * Read the system catalog |
boolean isNegative = ((valueBytes[0] & 0x80) != 0); | boolean isNegative = ((valueBytes[0] & 0x80) != 0); | ||||
// bit twiddling rules: | // 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()) { | if(isNegative == isAscending()) { | ||||
flipBytes(valueBytes); | 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); | bout.write(valueBytes); | ||||
} | } |
import java.nio.ByteBuffer; | import java.nio.ByteBuffer; | ||||
import java.nio.channels.FileChannel; | import java.nio.channels.FileChannel; | ||||
import java.nio.charset.Charset; | 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 | * Encapsulates constants describing a specific version of the Access Jet format | ||||
/** Maximum size of a text field */ | /** Maximum size of a text field */ | ||||
public static final short TEXT_FIELD_MAX_LENGTH = 255 * TEXT_FIELD_UNIT_SIZE; | 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; | private static final long OFFSET_VERSION = 20L; | ||||
/** Version code for Jet version 3 */ | /** Version code for Jet version 3 */ | ||||
private static final byte CODE_VERSION_3 = 0x0; | private static final byte CODE_VERSION_3 = 0x0; | ||||
/** Version code for Jet version 4 */ | /** Version code for Jet version 4 */ | ||||
private static final byte CODE_VERSION_4 = 0x1; | 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 | //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 | //populated by the subclass's constructor because they are final, and Java | ||||
public final int MAX_TABLE_NAME_LENGTH; | public final int MAX_TABLE_NAME_LENGTH; | ||||
public final int MAX_COLUMN_NAME_LENGTH; | public final int MAX_COLUMN_NAME_LENGTH; | ||||
public final int MAX_INDEX_NAME_LENGTH; | public final int MAX_INDEX_NAME_LENGTH; | ||||
public final boolean REVERSE_FIRST_BYTE_IN_DESC_NUMERIC_INDEXES; | |||||
public final Charset CHARSET; | 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_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 | * @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 { | public static JetFormat getFormat(FileChannel channel) throws IOException { | ||||
ByteBuffer buffer = ByteBuffer.allocate(1); | ByteBuffer buffer = ByteBuffer.allocate(1); | ||||
} | } | ||||
buffer.flip(); | buffer.flip(); | ||||
byte version = buffer.get(); | 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; | return VERSION_4; | ||||
} else if (version == CODE_VERSION_5) { | |||||
return VERSION_5; | |||||
} | } | ||||
throw new IOException("Unsupported version: " + version); | throw new IOException("Unsupported version: " + version); | ||||
} | } | ||||
MAX_TABLE_NAME_LENGTH = defineMaxTableNameLength(); | MAX_TABLE_NAME_LENGTH = defineMaxTableNameLength(); | ||||
MAX_COLUMN_NAME_LENGTH = defineMaxColumnNameLength(); | MAX_COLUMN_NAME_LENGTH = defineMaxColumnNameLength(); | ||||
MAX_INDEX_NAME_LENGTH = defineMaxIndexNameLength(); | MAX_INDEX_NAME_LENGTH = defineMaxIndexNameLength(); | ||||
REVERSE_FIRST_BYTE_IN_DESC_NUMERIC_INDEXES = defineReverseFirstByteInDescNumericIndexes(); | |||||
CHARSET = defineCharset(); | CHARSET = defineCharset(); | ||||
} | } | ||||
protected abstract Charset defineCharset(); | protected abstract Charset defineCharset(); | ||||
protected abstract boolean defineReverseFirstByteInDescNumericIndexes(); | |||||
protected abstract Map<Database.FileFormat,byte[]> getPossibleFileFormats(); | |||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return _name; | return _name; | ||||
} | } | ||||
private static final class Jet4Format extends JetFormat { | |||||
private static class Jet4Format extends JetFormat { | |||||
private Jet4Format() { | private Jet4Format() { | ||||
super("VERSION_4"); | |||||
this("VERSION_4"); | |||||
} | |||||
private Jet4Format(final String name) { | |||||
super(name); | |||||
} | } | ||||
@Override | @Override | ||||
@Override | @Override | ||||
protected int defineMaxIndexNameLength() { return 64; } | protected int defineMaxIndexNameLength() { return 64; } | ||||
@Override | |||||
protected boolean defineReverseFirstByteInDescNumericIndexes() { return false; } | |||||
@Override | @Override | ||||
protected Charset defineCharset() { return Charset.forName("UTF-16LE"); } | 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; | |||||
} | |||||
} | |||||
} | } |
new byte[]{PageTypes.INVALID, (byte)0, (byte)0, (byte)0}; | new byte[]{PageTypes.INVALID, (byte)0, (byte)0, (byte)0}; | ||||
/** Global usage map always lives on page 1 */ | /** 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 */ | /** 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 */ | /** Channel containing the database */ | ||||
private final FileChannel _channel; | private final FileChannel _channel; |
/** the current handler implementation for reading/writing the specific | /** the current handler implementation for reading/writing the specific | ||||
usage map type. note, this may change over time. */ | usage map type. note, this may change over time. */ | ||||
private Handler _handler; | 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 database database that contains this usage map | ||||
* @param tableBuffer Buffer that contains this map's declaration | * @param tableBuffer Buffer that contains this map's declaration | ||||
* @param pageNum Page number that this usage map is contained in | * @param pageNum Page number that this usage map is contained in | ||||
} else if (mapType == MAP_TYPE_REFERENCE) { | } else if (mapType == MAP_TYPE_REFERENCE) { | ||||
_handler = new ReferenceHandler(); | _handler = new ReferenceHandler(); | ||||
} else { | } else { | ||||
throw new IOException("Unrecognized map type: " + mapType); | |||||
throw new IOException(MSG_PREFIX_UNRECOGNIZED_MAP + mapType); | |||||
} | } | ||||
} | } | ||||
package com.healthmarketscience.jackcess; | package com.healthmarketscience.jackcess; | ||||
import java.io.File; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
import junit.framework.TestCase; | import junit.framework.TestCase; | ||||
import static com.healthmarketscience.jackcess.DatabaseTest.*; | import static com.healthmarketscience.jackcess.DatabaseTest.*; | ||||
import static com.healthmarketscience.jackcess.JetFormatTest.*; | |||||
/** | /** | ||||
public void testComplexIndex() throws Exception | 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 | 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 | // 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); | int nextInt = rand.nextInt(Integer.MAX_VALUE); | ||||
String nextVal = "" + nextInt + extraText; | String nextVal = "" + nextInt + extraText; | ||||
if(((i + 1) % 333) == 0) { | if(((i + 1) % 333) == 0) { | ||||
nextVal = null; | 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; | |||||
} | } | ||||
} | } | ||||
import junit.framework.TestCase; | import junit.framework.TestCase; | ||||
import static com.healthmarketscience.jackcess.JetFormatTest.*; | |||||
/** | /** | ||||
* @author James Ahlborn | * @author James Ahlborn | ||||
*/ | */ | ||||
public void test() throws Exception | 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(); | |||||
} | } | ||||
} | } |
package com.healthmarketscience.jackcess; | package com.healthmarketscience.jackcess; | ||||
import java.io.File; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import java.util.Collections; | import java.util.Collections; | ||||
import junit.framework.TestCase; | import junit.framework.TestCase; | ||||
import static com.healthmarketscience.jackcess.Database.*; | |||||
import static com.healthmarketscience.jackcess.DatabaseTest.*; | import static com.healthmarketscience.jackcess.DatabaseTest.*; | ||||
import static com.healthmarketscience.jackcess.JetFormatTest.*; | |||||
/** | /** | ||||
* @author James Ahlborn | * @author James Ahlborn | ||||
return expectedRows; | 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") | Table table = new TableBuilder("test") | ||||
.addColumn(new ColumnBuilder("id", DataType.LONG).toColumn()) | .addColumn(new ColumnBuilder("id", DataType.LONG).toColumn()) | ||||
} | } | ||||
return expectedRows; | 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"); | Table table = db.getTable("test"); | ||||
for(Map<String,Object> row : createUnorderedTestTableData()) { | for(Map<String,Object> row : createUnorderedTestTableData()) { | ||||
table.addRow(row.get("id"), row.get("value")); | table.addRow(row.get("id"), row.get("value")); | ||||
} | } | ||||
return db; | return db; | ||||
} | } | ||||
} | } | ||||
public void testSimple() throws Exception { | 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 | throws Exception | ||||
{ | { | ||||
if(expectedRows == null) { | if(expectedRows == null) { | ||||
} | } | ||||
public void testMove() throws Exception { | 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 | throws Exception | ||||
{ | { | ||||
if(expectedRows == null) { | if(expectedRows == null) { | ||||
} | } | ||||
public void testSearch() throws Exception { | 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, | private void doTestSearch(Table table, Cursor cursor, Index index, | ||||
} | } | ||||
public void testReverse() throws Exception { | 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 | throws Exception | ||||
{ | { | ||||
if(expectedRows == null) { | if(expectedRows == null) { | ||||
} | } | ||||
public void testLiveAddition() throws Exception { | 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, | private void doTestLiveAddition(Table table, | ||||
public void testLiveDeletion() throws Exception { | 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(2, cursor1.moveNextRows(2)); | ||||
assertEquals(3, cursor2.moveNextRows(3)); | assertEquals(3, cursor2.moveNextRows(3)); | ||||
} | } | ||||
public void testSimpleIndex() throws Exception { | 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 { | 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 { | 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 { | 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 { | 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 { | 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 { | 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 { | 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 { | 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 { | 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 { | 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 { | 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 | 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(); | |||||
} | |||||
} | } | ||||
} | } |
import junit.framework.TestCase; | import junit.framework.TestCase; | ||||
import static com.healthmarketscience.jackcess.Database.*; | |||||
import static com.healthmarketscience.jackcess.DatabaseTest.*; | import static com.healthmarketscience.jackcess.DatabaseTest.*; | ||||
/** | /** | ||||
public void testErrorHandler() throws Exception | 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") | @SuppressWarnings("unchecked") |
import junit.framework.TestCase; | import junit.framework.TestCase; | ||||
import static com.healthmarketscience.jackcess.Database.*; | |||||
import static com.healthmarketscience.jackcess.DatabaseTest.*; | import static com.healthmarketscience.jackcess.DatabaseTest.*; | ||||
/** | /** | ||||
public void testImportFromFile() throws Exception | 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 | 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"); | "\\t"); | ||||
} | |||||
} | } | ||||
public void testCopySqlHeaders() throws Exception | 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()); | |||||
} | |||||
} | } | ||||
package com.healthmarketscience.jackcess; | package com.healthmarketscience.jackcess; | ||||
import java.io.File; | |||||
import java.lang.reflect.Field; | import java.lang.reflect.Field; | ||||
import java.nio.ByteBuffer; | import java.nio.ByteBuffer; | ||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import junit.framework.TestCase; | import junit.framework.TestCase; | ||||
import static com.healthmarketscience.jackcess.DatabaseTest.*; | import static com.healthmarketscience.jackcess.DatabaseTest.*; | ||||
import static com.healthmarketscience.jackcess.JetFormatTest.*; | |||||
/** | /** | ||||
public void testIndexCodes() throws Exception | 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(); | // index.initialize(); | ||||
// System.out.println("Ind " + index); | // System.out.println("Ind " + index); | ||||
Cursor.Position curPos = cursor.getSavepoint().getCurrentPosition(); | Cursor.Position curPos = cursor.getSavepoint().getCurrentPosition(); | ||||
boolean success = false; | boolean success = false; | ||||
try { | try { | ||||
findRow(t, index, row, curPos); | |||||
findRow(testDB, t, index, row, curPos); | |||||
success = true; | success = true; | ||||
} finally { | } finally { | ||||
if(!success) { | if(!success) { | ||||
} | } | ||||
private static void findRow(Table t, Index index, | |||||
private static void findRow(final TestDB testDB, Table t, Index index, | |||||
Map<String,Object> expectedRow, | Map<String,Object> expectedRow, | ||||
Cursor.Position expectedPos) | Cursor.Position expectedPos) | ||||
throws Exception | throws Exception | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
fail("Could not find expected row " + expectedRow + " starting at " + | |||||
fail("testDB: " + testDB + ";\nCould not find expected row " + expectedRow + " starting at " + | |||||
entryToString(startPos)); | entryToString(startPos)); | ||||
} | } | ||||
public void x_testCreateIsoFile() throws Exception | 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 | 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 | 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 | 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) | private int addCombos(Table t, int rowNum, String s, char[] cs, int len) | ||||
public void x_testReadIsoMdb() throws Exception | 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 | 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) | private static String toByteString(String[] chars) |
package com.healthmarketscience.jackcess; | package com.healthmarketscience.jackcess; | ||||
import java.io.File; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import junit.framework.TestCase; | import junit.framework.TestCase; | ||||
import static com.healthmarketscience.jackcess.DatabaseTest.*; | import static com.healthmarketscience.jackcess.DatabaseTest.*; | ||||
import static com.healthmarketscience.jackcess.JetFormatTest.*; | |||||
/** | /** | ||||
* @author James Ahlborn | * @author James Ahlborn | ||||
} | } | ||||
public void testPrimaryKey() throws Exception { | 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 | 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 | 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 { | 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 | 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) | private void doTestIgnoreNulls(Database db, String tableName) | ||||
public void testUnique() throws Exception | 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) | Object... testData) | ||||
throws Exception | throws Exception | ||||
{ | { | ||||
} | } | ||||
public void testUniqueEntryCount() throws Exception { | 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 | 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) | private void checkIndexColumns(Table table, String... idxInfo) |
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(); | |||||
} | |||||
} | |||||
} |
package com.healthmarketscience.jackcess; | package com.healthmarketscience.jackcess; | ||||
import java.io.File; | |||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import java.util.List; | import java.util.List; | ||||
import junit.framework.TestCase; | import junit.framework.TestCase; | ||||
import static com.healthmarketscience.jackcess.DatabaseTest.*; | import static com.healthmarketscience.jackcess.DatabaseTest.*; | ||||
import static com.healthmarketscience.jackcess.JetFormatTest.*; | |||||
/** | /** | ||||
* @author James Ahlborn | * @author James Ahlborn | ||||
} | } | ||||
public void testSimple() throws Exception { | 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( | private void assertSameRelationships( |
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()); | |||||
} | |||||
} |
package com.healthmarketscience.jackcess.query; | package com.healthmarketscience.jackcess.query; | ||||
import java.io.File; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import static org.apache.commons.lang.SystemUtils.LINE_SEPARATOR; | import static org.apache.commons.lang.SystemUtils.LINE_SEPARATOR; | ||||
import static com.healthmarketscience.jackcess.query.QueryFormat.*; | import static com.healthmarketscience.jackcess.query.QueryFormat.*; | ||||
import static com.healthmarketscience.jackcess.JetFormatTest.*; | |||||
/** | /** | ||||
* @author James Ahlborn | * @author James Ahlborn | ||||
public void testReadQueries() throws Exception | 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 | private void doTestColumns(SelectQuery query) throws Exception |