From 957d85b4f12b71a8e86955fa5057afddbe181a1c Mon Sep 17 00:00:00 2001 From: James Ahlborn Date: Sun, 8 Mar 2015 19:24:02 +0000 Subject: [PATCH] lower log level for certain system table warnings; add contextual info to many errors/warnings: feature #29 git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@919 f203690c-595d-4dc9-a70b-905162fa7fd2 --- src/changes/changes.xml | 7 ++ .../jackcess/BatchUpdateException.java | 4 +- .../jackcess/ColumnBuilder.java | 55 ++++---- .../jackcess/IndexBuilder.java | 22 ++-- .../jackcess/impl/CalculatedColumnUtil.java | 8 +- .../jackcess/impl/ColumnImpl.java | 57 +++++---- .../jackcess/impl/ComplexColumnSupport.java | 17 +-- .../jackcess/impl/DatabaseImpl.java | 118 +++++++++++------- .../jackcess/impl/IndexData.java | 98 +++++++++++---- .../jackcess/impl/IndexImpl.java | 27 ++-- .../jackcess/impl/IndexPageCache.java | 108 +++++++++------- .../jackcess/impl/LongValueColumnImpl.java | 37 +++--- .../jackcess/impl/TableCreator.java | 8 ++ .../jackcess/impl/TableImpl.java | 72 +++++++---- .../jackcess/impl/query/QueryImpl.java | 44 ++++--- .../jackcess/util/Joiner.java | 6 +- 16 files changed, 432 insertions(+), 256 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 78bf4ea..0c866c0 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -21,6 +21,13 @@ selectively including different table types when iterating the tables in a Database. + + Lowered the log level for certain warnings related to system tables + which do not generally affect jackcess functionality. + + + Added contextual information to many errors and warnings. + diff --git a/src/main/java/com/healthmarketscience/jackcess/BatchUpdateException.java b/src/main/java/com/healthmarketscience/jackcess/BatchUpdateException.java index 1fb3426..ecb1fcf 100644 --- a/src/main/java/com/healthmarketscience/jackcess/BatchUpdateException.java +++ b/src/main/java/com/healthmarketscience/jackcess/BatchUpdateException.java @@ -32,8 +32,8 @@ public class BatchUpdateException extends JackcessException private final int _updateCount; - public BatchUpdateException(int updateCount, Throwable cause) { - super(cause); + public BatchUpdateException(int updateCount, String msg, Throwable cause) { + super(msg + ": " + cause, cause); _updateCount = updateCount; } diff --git a/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java b/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java index f013e3b..84092b4 100644 --- a/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java +++ b/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java @@ -368,80 +368,82 @@ public class ColumnBuilder { */ public void validate(JetFormat format) { if(getType() == null) { - throw new IllegalArgumentException("must have type"); + throw new IllegalArgumentException(withErrorContext("must have type")); } DatabaseImpl.validateIdentifierName( getName(), format.MAX_COLUMN_NAME_LENGTH, "column"); if(getType().isUnsupported()) { - throw new IllegalArgumentException( - "Cannot create column with unsupported type " + getType()); + throw new IllegalArgumentException(withErrorContext( + "Cannot create column with unsupported type " + getType())); } if(!format.isSupportedDataType(getType())) { - throw new IllegalArgumentException( - "Database format " + format + " does not support type " + getType()); + throw new IllegalArgumentException(withErrorContext( + "Database format " + format + " does not support type " + getType())); } if(!getType().isVariableLength()) { if(getLength() != getType().getFixedSize()) { if(getLength() < getType().getFixedSize()) { - throw new IllegalArgumentException("invalid fixed length size"); + throw new IllegalArgumentException(withErrorContext( + "invalid fixed length size")); } - LOG.warn("Column length " + getLength() + - " longer than expected fixed size " + - getType().getFixedSize()); + LOG.warn(withErrorContext( + "Column length " + getLength() + + " longer than expected fixed size " + getType().getFixedSize())); } } else if(!getType().isLongValue()) { if(!getType().isValidSize(getLength())) { - throw new IllegalArgumentException("var length out of range"); + throw new IllegalArgumentException(withErrorContext( + "var length out of range")); } } if(getType().getHasScalePrecision()) { if(!getType().isValidScale(getScale())) { - throw new IllegalArgumentException( + throw new IllegalArgumentException(withErrorContext( "Scale must be from " + getType().getMinScale() + " to " + - getType().getMaxScale() + " inclusive"); + getType().getMaxScale() + " inclusive")); } if(!getType().isValidPrecision(getPrecision())) { - throw new IllegalArgumentException( + throw new IllegalArgumentException(withErrorContext( "Precision must be from " + getType().getMinPrecision() + " to " + - getType().getMaxPrecision() + " inclusive"); + getType().getMaxPrecision() + " inclusive")); } } if(isAutoNumber()) { if(!getType().mayBeAutoNumber()) { - throw new IllegalArgumentException( - "Auto number column must be long integer or guid"); + throw new IllegalArgumentException(withErrorContext( + "Auto number column must be long integer or guid")); } } if(isCompressedUnicode()) { if(!getType().isTextual()) { - throw new IllegalArgumentException( - "Only textual columns allow unicode compression (text/memo)"); + throw new IllegalArgumentException(withErrorContext( + "Only textual columns allow unicode compression (text/memo)")); } } if(isHyperlink()) { if(getType() != DataType.MEMO) { - throw new IllegalArgumentException( - "Only memo columns can be hyperlinks"); + throw new IllegalArgumentException(withErrorContext( + "Only memo columns can be hyperlinks")); } } if(isCalculated()) { if(!format.isSupportedCalculatedDataType(getType())) { - throw new IllegalArgumentException( + throw new IllegalArgumentException(withErrorContext( "Database format " + format + " does not support calculated type " + - getType()); + getType())); } // must have an expression if(getProperty(PropertyMap.EXPRESSION_PROP) == null) { - throw new IllegalArgumentException( - "No expression provided for calculated type " + getType()); + throw new IllegalArgumentException(withErrorContext( + "No expression provided for calculated type " + getType())); } // must have result type (just fill in if missing) @@ -458,5 +460,8 @@ public class ColumnBuilder { // for backwards compat w/ old code return this; } - + + private String withErrorContext(String msg) { + return msg + "(Column=" + getName() + ")"; + } } diff --git a/src/main/java/com/healthmarketscience/jackcess/IndexBuilder.java b/src/main/java/com/healthmarketscience/jackcess/IndexBuilder.java index 4a55205..391debb 100644 --- a/src/main/java/com/healthmarketscience/jackcess/IndexBuilder.java +++ b/src/main/java/com/healthmarketscience/jackcess/IndexBuilder.java @@ -141,30 +141,32 @@ public class IndexBuilder getName(), format.MAX_INDEX_NAME_LENGTH, "index"); if(getColumns().isEmpty()) { - throw new IllegalArgumentException("index " + getName() + - " has no columns"); + throw new IllegalArgumentException(withErrorContext( + "index has no columns")); } if(getColumns().size() > IndexData.MAX_COLUMNS) { - throw new IllegalArgumentException("index " + getName() + - " has too many columns, max " + - IndexData.MAX_COLUMNS); + throw new IllegalArgumentException(withErrorContext( + "index has too many columns, max " + IndexData.MAX_COLUMNS)); } Set idxColNames = new HashSet(); for(Column col : getColumns()) { String idxColName = col.getName().toUpperCase(); if(!idxColNames.add(idxColName)) { - throw new IllegalArgumentException("duplicate column name " + - col.getName() + " in index " + - getName()); + throw new IllegalArgumentException(withErrorContext( + "duplicate column name " + col.getName() + " in index")); } if(!tableColNames.contains(idxColName)) { - throw new IllegalArgumentException("column named " + col.getName() + - " not found in table"); + throw new IllegalArgumentException(withErrorContext( + "column named " + col.getName() + " not found in table")); } } } + private String withErrorContext(String msg) { + return msg + "(Index=" + getName() + ")"; + } + /** * Information about a column in this index (name and ordering). */ diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/CalculatedColumnUtil.java b/src/main/java/com/healthmarketscience/jackcess/impl/CalculatedColumnUtil.java index 727818a..3b528f5 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/CalculatedColumnUtil.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/CalculatedColumnUtil.java @@ -55,9 +55,7 @@ class CalculatedColumnUtil * Creates the appropriate ColumnImpl class for a calculated column and * reads a column definition in from a buffer * - * @param table owning table - * @param buffer Buffer containing column definition - * @param offset Offset in the buffer at which the column definition starts + * @param args column construction info * @usage _advanced_method_ */ static ColumnImpl create(ColumnImpl.InitArgs args) throws IOException @@ -333,9 +331,9 @@ class CalculatedColumnUtil // check precision if(decVal.precision() > getType().getMaxPrecision()) { - throw new IOException( + throw new IOException(withErrorContext( "Numeric value is too big for specified precision " - + getType().getMaxPrecision() + ": " + decVal); + + getType().getMaxPrecision() + ": " + decVal)); } // convert to unscaled BigInteger, big-endian bytes diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java index 0565caf..676af4d 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/ColumnImpl.java @@ -282,7 +282,8 @@ public class ColumnImpl implements Column, Comparable { try { args.type = DataType.fromByte(colType); } catch(IOException e) { - LOG.warn("Unsupported column type " + colType); + LOG.warn(withErrorContext("Unsupported column type " + colType, + table.getDatabase(), table.getName(), name)); boolean variableLength = ((args.flags & FIXED_LEN_FLAG_MASK) == 0); args.type = (variableLength ? DataType.UNSUPPORTED_VARLEN : DataType.UNSUPPORTED_FIXEDLEN); @@ -496,8 +497,8 @@ public class ColumnImpl implements Column, Comparable { // cannot set autonumber validator (autonumber values are controlled // internally) if(newValidator != null) { - throw new IllegalArgumentException( - "Cannot set ColumnValidator for autonumber columns"); + throw new IllegalArgumentException(withErrorContext( + "Cannot set ColumnValidator for autonumber columns")); } // just leave default validator instance alone return; @@ -530,7 +531,7 @@ public class ColumnImpl implements Column, Comparable { case COMPLEX_TYPE: return new ComplexTypeAutoNumberGenerator(); default: - LOG.warn("Unknown auto number column type " + _type); + LOG.warn(withErrorContext("Unknown auto number column type " + _type)); return new UnsupportedAutoNumberGenerator(_type); } } @@ -603,7 +604,7 @@ public class ColumnImpl implements Column, Comparable { switch(getType()) { case BOOLEAN: - throw new IOException("Tried to read a boolean from data instead of null mask."); + throw new IOException(withErrorContext("Tried to read a boolean from data instead of null mask.")); case BYTE: return Byte.valueOf(buffer.get()); case INT: @@ -633,7 +634,7 @@ public class ColumnImpl implements Column, Comparable { case COMPLEX_TYPE: return new ComplexValueForeignKeyImpl(this, buffer.getInt()); default: - throw new IOException("Unrecognized data type: " + _type); + throw new IOException(withErrorContext("Unrecognized data type: " + _type)); } } @@ -644,11 +645,11 @@ public class ColumnImpl implements Column, Comparable { * @return BigDecimal representing the monetary value * @throws IOException if the value cannot be parsed */ - private static BigDecimal readCurrencyValue(ByteBuffer buffer) + private BigDecimal readCurrencyValue(ByteBuffer buffer) throws IOException { if(buffer.remaining() != 8) { - throw new IOException("Invalid money value."); + throw new IOException(withErrorContext("Invalid money value")); } return new BigDecimal(BigInteger.valueOf(buffer.getLong(0)), 4); @@ -735,9 +736,9 @@ public class ColumnImpl implements Column, Comparable { // check precision if(decVal.precision() > getPrecision()) { - throw new IOException( + throw new IOException(withErrorContext( "Numeric value is too big for specified precision " - + getPrecision() + ": " + decVal); + + getPrecision() + ": " + decVal)); } // convert to unscaled BigInteger, big-endian bytes @@ -754,7 +755,7 @@ public class ColumnImpl implements Column, Comparable { } } - static byte[] toUnscaledByteArray(BigDecimal decVal, int maxByteLen) + byte[] toUnscaledByteArray(BigDecimal decVal, int maxByteLen) throws IOException { // convert to unscaled BigInteger, big-endian bytes @@ -766,7 +767,8 @@ public class ColumnImpl implements Column, Comparable { // with unsigned values, so we can drop the extra leading 0 intValBytes = ByteUtil.copyOf(intValBytes, 1, maxByteLen); } else { - throw new IOException("Too many bytes for valid BigInteger?"); + throw new IOException(withErrorContext( + "Too many bytes for valid BigInteger?")); } } else if(intValBytes.length < maxByteLen) { intValBytes = ByteUtil.copyOf(intValBytes, 0, maxByteLen, @@ -910,12 +912,12 @@ public class ColumnImpl implements Column, Comparable { /** * Writes a GUID value. */ - private static void writeGUIDValue(ByteBuffer buffer, Object value) + private void writeGUIDValue(ByteBuffer buffer, Object value) throws IOException { Matcher m = GUID_PATTERN.matcher(toCharSequence(value)); if(!m.matches()) { - throw new IOException("Invalid GUID: " + value); + throw new IOException(withErrorContext("Invalid GUID: " + value)); } ByteBuffer origBuffer = null; @@ -1017,8 +1019,8 @@ public class ColumnImpl implements Column, Comparable { // should already be "encoded" break; default: - throw new RuntimeException("unexpected inline var length type: " + - getType()); + throw new RuntimeException(withErrorContext( + "unexpected inline var length type: " + getType())); } ByteBuffer buffer = ByteBuffer.wrap(toByteArray(obj)).order(order); @@ -1099,13 +1101,15 @@ public class ColumnImpl implements Column, Comparable { case UNSUPPORTED_FIXEDLEN: byte[] bytes = toByteArray(obj); if(bytes.length != getLength()) { - throw new IOException("Invalid fixed size binary data, size " - + getLength() + ", got " + bytes.length); + throw new IOException(withErrorContext( + "Invalid fixed size binary data, size " + + getLength() + ", got " + bytes.length)); } buffer.put(bytes); break; default: - throw new IOException("Unsupported data type: " + getType()); + throw new IOException(withErrorContext( + "Unsupported data type: " + getType())); } return buffer; } @@ -1205,9 +1209,10 @@ public class ColumnImpl implements Column, Comparable { { CharSequence text = toCharSequence(obj); if((text.length() > maxChars) || (text.length() < minChars)) { - throw new IOException("Text is wrong length for " + getType() + + throw new IOException(withErrorContext( + "Text is wrong length for " + getType() + " column, max " + maxChars - + ", min " + minChars + ", got " + text.length()); + + ", min " + minChars + ", got " + text.length())); } // may only compress if column type allows it @@ -1759,6 +1764,16 @@ public class ColumnImpl implements Column, Comparable { } } + String withErrorContext(String msg) { + return withErrorContext(msg, getDatabase(), getTable().getName(), getName()); + } + + private static String withErrorContext( + String msg, DatabaseImpl db, String tableName, String colName) { + return msg + " (Db=" + db.getName() + ";Table=" + tableName + ";Column=" + + colName + ")"; + } + /** * Date subclass which stashes the original date bits, in case we attempt to * re-write the value (will not lose precision). diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/ComplexColumnSupport.java b/src/main/java/com/healthmarketscience/jackcess/impl/ComplexColumnSupport.java index 469a720..7272f4a 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/ComplexColumnSupport.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/ComplexColumnSupport.java @@ -74,16 +74,16 @@ public class ComplexColumnSupport IndexCursor cursor = CursorBuilder.createCursor( complexColumns.getPrimaryKeyIndex()); if(!cursor.findFirstRowByEntry(complexTypeId)) { - throw new IOException( + throw new IOException(column.withErrorContext( "Could not find complex column info for complex column with id " + - complexTypeId); + complexTypeId)); } Row cColRow = cursor.getCurrentRow(); int tableId = cColRow.getInt(COL_TABLE_ID); if(tableId != column.getTable().getTableDefPageNumber()) { - throw new IOException( + throw new IOException(column.withErrorContext( "Found complex column for table " + tableId + " but expected table " + - column.getTable().getTableDefPageNumber()); + column.getTable().getTableDefPageNumber())); } int flatTableId = cColRow.getInt(COL_FLAT_TABLE_ID); int typeObjId = cColRow.getInt(COL_COMPLEX_TYPE_OBJECT_ID); @@ -92,9 +92,9 @@ public class ComplexColumnSupport TableImpl flatTable = db.getTable(flatTableId); if((typeObjTable == null) || (flatTable == null)) { - throw new IOException( + throw new IOException(column.withErrorContext( "Could not find supporting tables (" + typeObjId + ", " + flatTableId - + ") for complex column with id " + complexTypeId); + + ") for complex column with id " + complexTypeId)); } // we inspect the structore of the "type table" to determine what kind of @@ -110,9 +110,10 @@ public class ComplexColumnSupport flatTable); } - LOG.warn("Unsupported complex column type " + typeObjTable.getName()); + LOG.warn(column.withErrorContext( + "Unsupported complex column type " + typeObjTable.getName())); return new UnsupportedColumnInfoImpl(column, complexTypeId, typeObjTable, - flatTable); + flatTable); } diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java index bb3bfac..2823f62 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java @@ -257,6 +257,8 @@ public class DatabaseImpl implements Database /** the File of the database */ private final File _file; + /** the simple name of the database */ + private final String _name; /** Buffer to hold database pages */ private ByteBuffer _buffer; /** ID of the Tables system object */ @@ -388,12 +390,12 @@ public class DatabaseImpl implements Database if(jetFormat.READ_ONLY) { throw new IOException("jet format '" + jetFormat + - "' does not support writing"); + "' does not support writing for " + mdbFile); } } DatabaseImpl db = new DatabaseImpl(mdbFile, channel, closeChannel, autoSync, - null, charset, timeZone, provider); + null, charset, timeZone, provider); success = true; return db; @@ -433,7 +435,7 @@ public class DatabaseImpl implements Database FileFormatDetails details = getFileFormatDetails(fileFormat); if (details.getFormat().READ_ONLY) { throw new IOException("file format " + fileFormat + - " does not support writing"); + " does not support writing for " + mdbFile); } boolean closeChannel = false; @@ -504,6 +506,7 @@ public class DatabaseImpl implements Database throws IOException { _file = file; + _name = getName(file); _format = JetFormat.getFormat(channel); _charset = ((charset == null) ? getDefaultCharset(_format) : charset); _columnOrder = getDefaultColumnOrder(); @@ -526,6 +529,10 @@ public class DatabaseImpl implements Database return _file; } + public String getName() { + return _name; + } + /** * @usage _advanced_method_ */ @@ -554,12 +561,7 @@ public class DatabaseImpl implements Database */ public TableImpl getAccessControlEntries() throws IOException { if(_accessControlEntries == null) { - _accessControlEntries = getSystemTable(TABLE_SYSTEM_ACES); - if(_accessControlEntries == null) { - throw new IOException("Could not find system table " + - TABLE_SYSTEM_ACES); - } - + _accessControlEntries = getRequiredSystemTable(TABLE_SYSTEM_ACES); } return _accessControlEntries; } @@ -570,11 +572,7 @@ public class DatabaseImpl implements Database */ public TableImpl getSystemComplexColumns() throws IOException { if(_complexCols == null) { - _complexCols = getSystemTable(TABLE_SYSTEM_COMPLEX_COLS); - if(_complexCols == null) { - throw new IOException("Could not find system table " + - TABLE_SYSTEM_COMPLEX_COLS); - } + _complexCols = getRequiredSystemTable(TABLE_SYSTEM_COMPLEX_COLS); } return _complexCols; } @@ -735,7 +733,8 @@ public class DatabaseImpl implements Database _fileFormat = possibleFileFormats.get(accessVersion); if(_fileFormat == null) { - throw new IllegalStateException("Could not determine FileFormat"); + throw new IllegalStateException(withErrorContext( + "Could not determine FileFormat")); } } } @@ -839,8 +838,11 @@ public class DatabaseImpl implements Database .setColumnMatcher(CaseInsensitiveColumnMatcher.INSTANCE) .toIndexCursor()); } catch(IllegalArgumentException e) { - LOG.info("Could not find expected index on table " + - _systemCatalog.getName()); + if(LOG.isDebugEnabled()) { + LOG.debug(withErrorContext( + "Could not find expected index on table " + + _systemCatalog.getName())); + } // use table scan instead _tableFinder = new FallbackTableFinder( _systemCatalog.newCursor() @@ -852,11 +854,13 @@ public class DatabaseImpl implements Database SYSTEM_OBJECT_NAME_TABLES); if(_tableParentId == null) { - throw new IOException("Did not find required parent table id"); + throw new IOException(withErrorContext( + "Did not find required parent table id")); } if (LOG.isDebugEnabled()) { - LOG.debug("Finished reading system catalog. Tables: " + getTableNames()); + LOG.debug(withErrorContext( + "Finished reading system catalog. Tables: " + getTableNames())); } } @@ -996,8 +1000,8 @@ public class DatabaseImpl implements Database throws IOException { if(lookupTable(name) != null) { - throw new IllegalArgumentException( - "Cannot create table with name of existing table"); + throw new IllegalArgumentException(withErrorContext( + "Cannot create table with name of existing table '" + name + "'")); } new TableCreator(this, name, columns, indexes).createTable(); @@ -1008,8 +1012,9 @@ public class DatabaseImpl implements Database throws IOException { if(lookupTable(name) != null) { - throw new IllegalArgumentException( - "Cannot create linked table with name of existing table"); + throw new IllegalArgumentException(withErrorContext( + "Cannot create linked table with name of existing table '" + name + + "'")); } validateIdentifierName(name, getFormat().MAX_TABLE_NAME_LENGTH, "table"); @@ -1060,7 +1065,8 @@ public class DatabaseImpl implements Database { int nameCmp = table1.getName().compareTo(table2.getName()); if(nameCmp == 0) { - throw new IllegalArgumentException("Must provide two different tables"); + throw new IllegalArgumentException(withErrorContext( + "Must provide two different tables")); } if(nameCmp > 0) { // we "order" the two tables given so that we will return a collection @@ -1078,7 +1084,7 @@ public class DatabaseImpl implements Database throws IOException { if(table == null) { - throw new IllegalArgumentException("Must provide a table"); + throw new IllegalArgumentException(withErrorContext("Must provide a table")); } // since we are getting relationships specific to certain table include // all tables @@ -1103,10 +1109,7 @@ public class DatabaseImpl implements Database { // the relationships table does not get loaded until first accessed if(_relationships == null) { - _relationships = getSystemTable(TABLE_SYSTEM_RELATIONSHIPS); - if(_relationships == null) { - throw new IOException("Could not find system relationships table"); - } + _relationships = getRequiredSystemTable(TABLE_SYSTEM_RELATIONSHIPS); } List relationships = new ArrayList(); @@ -1132,10 +1135,7 @@ public class DatabaseImpl implements Database { // the queries table does not get loaded until first accessed if(_queries == null) { - _queries = getSystemTable(TABLE_SYSTEM_QUERIES); - if(_queries == null) { - throw new IOException("Could not find system queries table"); - } + _queries = getRequiredSystemTable(TABLE_SYSTEM_QUERIES); } // find all the queries from the system catalog @@ -1159,8 +1159,9 @@ public class DatabaseImpl implements Database QueryImpl.Row queryRow = new QueryImpl.Row(row); List queryRows = queryRowMap.get(queryRow.objectId); if(queryRows == null) { - LOG.warn("Found rows for query with id " + queryRow.objectId + - " missing from system catalog"); + LOG.warn(withErrorContext( + "Found rows for query with id " + queryRow.objectId + + " missing from system catalog")); continue; } queryRows.add(queryRow); @@ -1184,6 +1185,16 @@ public class DatabaseImpl implements Database return getTable(tableName, true); } + private TableImpl getRequiredSystemTable(String tableName) throws IOException + { + TableImpl table = getSystemTable(tableName); + if(table == null) { + throw new IOException(withErrorContext( + "Could not find system table " + tableName)); + } + return table; + } + public PropertyMap getDatabaseProperties() throws IOException { if(_dbPropMaps == null) { _dbPropMaps = getPropertiesForDbObject(OBJECT_NAME_DB_PROPS); @@ -1234,7 +1245,8 @@ public class DatabaseImpl implements Database _dbParentId = _tableFinder.findObjectId(DB_PARENT_ID, SYSTEM_OBJECT_NAME_DATABASES); if(_dbParentId == null) { - throw new IOException("Did not find required parent db id"); + throw new IOException(withErrorContext( + "Did not find required parent db id")); } } @@ -1483,9 +1495,9 @@ public class DatabaseImpl implements Database _pageChannel.readPage(buffer, pageNumber); byte pageType = buffer.get(0); if (pageType != PageTypes.TABLE_DEF) { - throw new IOException( + throw new IOException(withErrorContext( "Looking for " + name + " at page " + pageNumber + - ", but page type is " + pageType); + ", but page type is " + pageType)); } return _tableCache.put( new TableImpl(this, buffer, pageNumber, name, flags)); @@ -1498,7 +1510,7 @@ public class DatabaseImpl implements Database * Creates a Cursor restricted to the given column value if possible (using * an existing index), otherwise a simple table cursor. */ - private static Cursor createCursorWithOptionalIndex( + private Cursor createCursorWithOptionalIndex( TableImpl table, String colName, Object colValue) throws IOException { @@ -1508,7 +1520,10 @@ public class DatabaseImpl implements Database .setSpecificEntry(colValue) .toCursor(); } catch(IllegalArgumentException e) { - LOG.info("Could not find expected index on table " + table.getName()); + if(LOG.isDebugEnabled()) { + LOG.debug(withErrorContext( + "Could not find expected index on table " + table.getName())); + } } // use table scan instead return CursorImpl.createCursor(table); @@ -1556,13 +1571,14 @@ public class DatabaseImpl implements Database // additional identifier validation if(INVALID_IDENTIFIER_CHARS.matcher(name).find()) { throw new IllegalArgumentException( - identifierType + " name contains invalid characters"); + identifierType + " name '" + name + "' contains invalid characters"); } // cannot start with spaces if(name.charAt(0) == ' ') { throw new IllegalArgumentException( - identifierType + " name cannot start with a space character"); + identifierType + " name '" + name + + "' cannot start with a space character"); } } @@ -1818,6 +1834,21 @@ public class DatabaseImpl implements Database FILE_FORMAT_DETAILS.put(fileFormat, new FileFormatDetails(emptyFile, format)); } + private static String getName(File file) { + if(file == null) { + return ""; + } + return file.getName(); + } + + private String withErrorContext(String msg) { + return withErrorContext(msg, getName()); + } + + private static String withErrorContext(String msg, String dbName) { + return msg + " (Db=" + dbName + ")"; + } + /** * Utility class for storing table page number and actual name. */ @@ -1989,7 +2020,8 @@ public class DatabaseImpl implements Database int maxSynthId = findMaxSyntheticId(); if(maxSynthId >= -1) { // bummer, no more ids available - throw new IllegalStateException("Too many database objects!"); + throw new IllegalStateException(withErrorContext( + "Too many database objects!")); } return maxSynthId + 1; } diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java b/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java index 579deef..a6970a9 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/IndexData.java @@ -144,6 +144,8 @@ public class IndexData { }; + /** name, generated on demand */ + private String _name; /** owning table */ private final TableImpl _table; /** 0-based index data number */ @@ -212,6 +214,23 @@ public class IndexData { return new IndexData(table, number, uniqueEntryCount, uniqueEntryCountOffset); } + public String getName() { + if(_name == null) { + if(_indexes.size() == 1) { + _name = _indexes.get(0).getName(); + } else if(!_indexes.isEmpty()) { + List names = new ArrayList(_indexes.size()); + for(Index idx : _indexes) { + names.add(idx.getName()); + } + _name = names.toString(); + } else { + _name = String.valueOf(_number); + } + } + return _name; + } + public TableImpl getTable() { return _table; } @@ -260,6 +279,9 @@ public class IndexData { // also, keep track of whether or not this is a primary key index _primaryKey |= index.isPrimaryKey(); } + + // force name to be regenerated + _name = null; } public byte getIndexFlags() { @@ -323,9 +345,15 @@ public class IndexData { return _rootPageNumber; } - private void setUnsupportedReason(String reason) { - _unsupportedReason = reason; - LOG.warn(reason + ", making read-only"); + private void setUnsupportedReason(String reason, ColumnImpl col) { + _unsupportedReason = withErrorContext(reason); + if(!col.getTable().isSystem()) { + LOG.warn(_unsupportedReason + ", making read-only"); + } else { + if(LOG.isDebugEnabled()) { + LOG.debug(_unsupportedReason + ", making read-only"); + } + } } String getUnsupportedReason() { @@ -429,8 +457,9 @@ public class IndexData { } } if(idxCol == null) { - throw new IOException("Could not find column with number " - + columnNumber + " for index"); + throw new IOException(withErrorContext( + "Could not find column with number " + + columnNumber + " for index")); } _columns.add(newColumnDescriptor(idxCol, colFlags)); } @@ -447,8 +476,8 @@ public class IndexData { /** * Writes the index row count definitions into a table definition buffer. + * @param creator description of the indexes to write * @param buffer Buffer to write to - * @param indexes List of IndexBuilders to write definitions for */ protected static void writeRowCountDefinitions( TableCreator creator, ByteBuffer buffer) @@ -460,8 +489,8 @@ public class IndexData { /** * Writes the index definitions into a table definition buffer. + * @param creator description of the indexes to write * @param buffer Buffer to write to - * @param indexes List of IndexBuilders to write definitions for */ protected static void writeDefinitions( TableCreator creator, ByteBuffer buffer) @@ -497,7 +526,9 @@ public class IndexData { if(columnNumber == COLUMN_UNUSED) { // should never happen as this is validated before throw new IllegalArgumentException( - "Column with name " + idxCol.getName() + " not found"); + withErrorContext( + "Column with name " + idxCol.getName() + " not found", + creator.getDatabase(), creator.getName(), idx.getName())); } } @@ -550,9 +581,9 @@ public class IndexData { return change; } if(isBackingPrimaryKey() && (nullCount > 0)) { - throw new ConstraintViolationException( + throw new ConstraintViolationException(withErrorContext( "Null value found in row " + Arrays.asList(row) + - " for primary key index " + this); + " for primary key index")); } // make sure we've parsed the entries @@ -589,9 +620,9 @@ public class IndexData { ((prevPos != null) && newEntry.equalsEntryBytes(prevPos.getEntry()))); if(isUnique() && !isNullEntry && isDupeEntry) { - throw new ConstraintViolationException( + throw new ConstraintViolationException(withErrorContext( "New row " + Arrays.asList(row) + - " violates uniqueness constraint for index " + this); + " violates uniqueness constraint for index")); } change.setAddRow(newEntry, dataPage, idx, isDupeEntry); @@ -617,7 +648,7 @@ public class IndexData { } ++_modCount; } else { - LOG.warn("Added duplicate index entry " + oldEntry); + LOG.warn(withErrorContext("Added duplicate index entry " + oldEntry)); } } @@ -682,8 +713,9 @@ public class IndexData { if(removedEntry != null) { ++_modCount; } else { - LOG.warn("Failed removing index entry " + oldEntry + " for row: " + - Arrays.asList(row)); + LOG.warn(withErrorContext( + "Failed removing index entry " + oldEntry + " for row: " + + Arrays.asList(row))); } return removedEntry; } @@ -906,9 +938,9 @@ public class IndexData { public Object[] constructIndexRowFromEntry(Object... values) { if(values.length != _columns.size()) { - throw new IllegalArgumentException( + throw new IllegalArgumentException(withErrorContext( "Wrong number of column values given " + values.length + - ", expected " + _columns.size()); + ", expected " + _columns.size())); } int valIdx = 0; Object[] idxRow = new Object[getTable().getColumnCount()]; @@ -978,7 +1010,7 @@ public class IndexData { throws IOException { if(dataPage.getCompressedEntrySize() > _maxPageEntrySize) { - throw new IllegalStateException("data page is too large"); + throw new IllegalStateException(withErrorContext("data page is too large")); } ByteBuffer buffer = _indexBufferH.getPageBuffer(getPageChannel()); @@ -1082,8 +1114,9 @@ public class IndexData { Entry entry = newEntry(curEntryBuffer, curEntryLen, isLeaf); if(prevEntry.compareTo(entry) >= 0) { - throw new IOException("Unexpected order in index entries, " + - prevEntry + " >= " + entry); + throw new IOException(withErrorContext( + "Unexpected order in index entries, " + + prevEntry + " >= " + entry)); } entries.add(entry); @@ -1151,7 +1184,7 @@ public class IndexData { /** * Determines if the given index page is a leaf or node page. */ - private static boolean isLeafPage(ByteBuffer buffer) + private boolean isLeafPage(ByteBuffer buffer) throws IOException { byte pageType = buffer.get(0); @@ -1160,7 +1193,7 @@ public class IndexData { } else if(pageType == PageTypes.INDEX_NODE) { return false; } - throw new IOException("Unexpected page type " + pageType); + throw new IOException(withErrorContext("Unexpected page type " + pageType)); } /** @@ -1305,7 +1338,7 @@ public class IndexData { } // unsupported sort order setUnsupportedReason("unsupported collating sort order " + sortOrder + - " for text index"); + " for text index", col); return new ReadOnlyColumnDescriptor(col, flags); case INT: case LONG: @@ -1330,7 +1363,7 @@ public class IndexData { default: // we can't modify this index at this point in time setUnsupportedReason("unsupported data type " + col.getType() + - " for index"); + " for index", col); return new ReadOnlyColumnDescriptor(col, flags); } } @@ -1338,8 +1371,7 @@ public class IndexData { /** * Returns the EntryType based on the given entry info. */ - private static EntryType determineEntryType(byte[] entryBytes, - RowIdImpl rowId) + private static EntryType determineEntryType(byte[] entryBytes, RowIdImpl rowId) { if(entryBytes != null) { return ((rowId.getType() == RowIdImpl.Type.NORMAL) ? @@ -1368,6 +1400,17 @@ public class IndexData { int entryMaskSize = (format.SIZE_INDEX_ENTRY_MASK * 8); return Math.min(pageDataSize, entryMaskSize); } + + String withErrorContext(String msg) { + return withErrorContext(msg, getTable().getDatabase(), getTable().getName(), + getName()); + } + + private static String withErrorContext(String msg, DatabaseImpl db, + String tableName, String idxName) { + return msg + " (Db=" + db.getName() + ";Table=" + tableName + + ";Index=" + idxName + ")"; + } /** * Information about the columns in an index. Also encodes new index @@ -2227,7 +2270,8 @@ public class IndexData { } else if(_lastPos.equalsEntry(entry)) { return _lastPos; } else { - throw new IllegalArgumentException("Invalid entry given " + entry); + throw new IllegalArgumentException( + withErrorContext("Invalid entry given " + entry)); } } diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java index 0f3bb72..e602441 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java @@ -176,8 +176,8 @@ public class IndexImpl implements Index, Comparable _reference.getOtherTablePageNumber()); if(refTable == null) { - throw new IOException("Reference to missing table " + - _reference.getOtherTablePageNumber()); + throw new IOException(withErrorContext( + "Reference to missing table " + _reference.getOtherTablePageNumber())); } IndexImpl refIndex = null; @@ -190,8 +190,9 @@ public class IndexImpl implements Index, Comparable } if(refIndex == null) { - throw new IOException("Reference to missing index " + idxNumber + - " on table " + refTable.getName()); + throw new IOException(withErrorContext( + "Reference to missing index " + idxNumber + + " on table " + refTable.getName())); } // finally verify that we found the expected index (should reference this @@ -201,9 +202,9 @@ public class IndexImpl implements Index, Comparable (otherRef.getOtherTablePageNumber() != getTable().getTableDefPageNumber()) || (otherRef.getOtherIndexNumber() != _indexNumber)) { - throw new IOException("Found unexpected index " + refIndex.getName() + - " on table " + refTable.getName() + - " with reference " + otherRef); + throw new IOException(withErrorContext( + "Found unexpected index " + refIndex.getName() + + " on table " + refTable.getName() + " with reference " + otherRef)); } return refIndex; @@ -335,8 +336,8 @@ public class IndexImpl implements Index, Comparable /** * Writes the logical index definitions into a table definition buffer. + * @param creator description of the indexes to write * @param buffer Buffer to write to - * @param indexes List of IndexBuilders to write definitions for */ protected static void writeDefinitions( TableCreator creator, ByteBuffer buffer) @@ -363,6 +364,16 @@ public class IndexImpl implements Index, Comparable } } + private String withErrorContext(String msg) { + return withErrorContext(msg, getTable().getDatabase(), getName()); + } + + private static String withErrorContext(String msg, DatabaseImpl db, + String idxName) { + return msg + " (Db=" + db.getName() + ";Index=" + idxName + ")"; + } + + /** * Information about a foreign key reference defined in an index (when * referential integrity should be enforced). diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/IndexPageCache.java b/src/main/java/com/healthmarketscience/jackcess/impl/IndexPageCache.java index c741a8d..cf58e6a 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/IndexPageCache.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/IndexPageCache.java @@ -204,8 +204,8 @@ public class IndexPageCache { for(CacheDataPage cacheDataPage : _modifiedPages) { if(cacheDataPage._extra._entryView.isEmpty()) { - throw new IllegalStateException("Unexpected empty page " + - cacheDataPage); + throw new IllegalStateException(withErrorContext( + "Unexpected empty page " + cacheDataPage)); } writeDataPage(cacheDataPage); } @@ -358,7 +358,8 @@ public class IndexPageCache break; } default: - throw new RuntimeException("unknown update type " + upType); + throw new RuntimeException(withErrorContext( + "unknown update type " + upType)); } boolean updateLast = (oldLastEntry != dpExtra._entryView.getLast()); @@ -406,13 +407,13 @@ public class IndexPageCache DataPageExtra dpExtra = cacheDataPage._extra; if(dpMain.hasChildTail()) { - throw new IllegalStateException("Still has child tail?"); + throw new IllegalStateException(withErrorContext("Still has child tail?")); } if(dpExtra._totalEntrySize != 0) { - throw new IllegalStateException("Empty page but size is not 0? " + - dpExtra._totalEntrySize + ", " + - cacheDataPage); + throw new IllegalStateException(withErrorContext( + "Empty page but size is not 0? " + dpExtra._totalEntrySize + ", " + + cacheDataPage)); } if(dpMain.isRoot()) { @@ -536,21 +537,22 @@ public class IndexPageCache break; default: - throw new RuntimeException("unknown update type " + upType); + throw new RuntimeException(withErrorContext( + "unknown update type " + upType)); } if(idx < 0) { if(expectFound) { - throw new IllegalStateException( + throw new IllegalStateException(withErrorContext( "Could not find child entry in parent; childEntry " + oldEntry + - "; parent " + parentDataPage); + "; parent " + parentDataPage)); } idx = missingIndexToInsertionPoint(idx); } else { if(!expectFound) { - throw new IllegalStateException( + throw new IllegalStateException(withErrorContext( "Unexpectedly found child entry in parent; childEntry " + - newEntry + "; parent " + parentDataPage); + newEntry + "; parent " + parentDataPage)); } } updateEntry(parentDataPage, idx, newEntry, upType); @@ -596,11 +598,11 @@ public class IndexPageCache * @throws IllegalStateException if the entry type does not match the page * type */ - private static void validateEntryForPage(DataPageMain dpMain, Entry entry) { + private void validateEntryForPage(DataPageMain dpMain, Entry entry) { if(dpMain._leaf != entry.isLeafEntry()) { - throw new IllegalStateException( + throw new IllegalStateException(withErrorContext( "Trying to update page with wrong entry type; pageLeaf " + - dpMain._leaf + ", entryLeaf " + entry.isLeafEntry()); + dpMain._leaf + ", entryLeaf " + entry.isLeafEntry())); } } @@ -619,8 +621,8 @@ public class IndexPageCache int numEntries = origExtra._entries.size(); if(numEntries < 2) { - throw new IllegalStateException( - "Cannot split page with less than 2 entries " + origDataPage); + throw new IllegalStateException(withErrorContext( + "Cannot split page with less than 2 entries " + origDataPage)); } if(origMain.isRoot()) { @@ -702,7 +704,8 @@ public class IndexPageCache DataPageExtra rootExtra = rootDataPage._extra; if(!rootMain.isRoot()) { - throw new IllegalArgumentException("should be called with root, duh"); + throw new IllegalArgumentException(withErrorContext( + "should be called with root, duh")); } CacheDataPage newDataPage = @@ -1000,20 +1003,21 @@ public class IndexPageCache * * @param dpExtra the entries to validate */ - private static void validateEntries(DataPageExtra dpExtra) throws IOException { + private void validateEntries(DataPageExtra dpExtra) throws IOException { int entrySize = 0; Entry prevEntry = IndexData.FIRST_ENTRY; for(Entry e : dpExtra._entries) { entrySize += e.size(); if(prevEntry.compareTo(e) >= 0) { - throw new IOException("Unexpected order in index entries, " + - prevEntry + " >= " + e); + throw new IOException(withErrorContext( + "Unexpected order in index entries, " + prevEntry + " >= " + e)); } prevEntry = e; } if(entrySize != dpExtra._totalEntrySize) { - throw new IllegalStateException("Expected size " + entrySize + - " but was " + dpExtra._totalEntrySize); + throw new IllegalStateException(withErrorContext( + "Expected size " + entrySize + + " but was " + dpExtra._totalEntrySize)); } } @@ -1028,12 +1032,14 @@ public class IndexPageCache int childTailPageNumber = dpMain._childTailPageNumber; if(dpMain._leaf) { if(childTailPageNumber != INVALID_INDEX_PAGE_NUMBER) { - throw new IllegalStateException("Leaf page has tail " + dpMain); + throw new IllegalStateException(withErrorContext( + "Leaf page has tail " + dpMain)); } return; } if((dpExtra._entryView.size() == 1) && dpMain.hasChildTail()) { - throw new IllegalStateException("Single child is tail " + dpMain); + throw new IllegalStateException(withErrorContext( + "Single child is tail " + dpMain)); } for(Entry e : dpExtra._entryView) { validateEntryForPage(dpMain, e); @@ -1042,19 +1048,19 @@ public class IndexPageCache if(childMain != null) { if(childMain._parentPageNumber != null) { if(childMain._parentPageNumber != dpMain._pageNumber) { - throw new IllegalStateException("Child's parent is incorrect " + - childMain); + throw new IllegalStateException(withErrorContext( + "Child's parent is incorrect " + childMain)); } - boolean expectTail = ((int)subPageNumber == childTailPageNumber); + boolean expectTail = (subPageNumber == childTailPageNumber); if(expectTail != childMain._tail) { - throw new IllegalStateException("Child tail status incorrect " + - childMain); + throw new IllegalStateException(withErrorContext( + "Child tail status incorrect " + childMain)); } } Entry lastEntry = childMain.getExtra()._entryView.getLast(); if(e.compareTo(lastEntry) != 0) { - throw new IllegalStateException("Invalid entry " + e + - " but child is " + lastEntry); + throw new IllegalStateException(withErrorContext( + "Invalid entry " + e + " but child is " + lastEntry)); } } } @@ -1068,18 +1074,18 @@ public class IndexPageCache private void validatePeers(DataPageMain dpMain) throws IOException { DataPageMain prevMain = _dataPages.get(dpMain._prevPageNumber); if(prevMain != null) { - if((int)prevMain._nextPageNumber != dpMain._pageNumber) { - throw new IllegalStateException("Prev page " + prevMain + - " does not ref " + dpMain); + if(prevMain._nextPageNumber != dpMain._pageNumber) { + throw new IllegalStateException(withErrorContext( + "Prev page " + prevMain + " does not ref " + dpMain)); } validatePeerStatus(dpMain, prevMain); } DataPageMain nextMain = _dataPages.get(dpMain._nextPageNumber); if(nextMain != null) { - if((int)nextMain._prevPageNumber != dpMain._pageNumber) { - throw new IllegalStateException("Next page " + nextMain + - " does not ref " + dpMain); + if(nextMain._prevPageNumber != dpMain._pageNumber) { + throw new IllegalStateException(withErrorContext( + "Next page " + nextMain + " does not ref " + dpMain)); } validatePeerStatus(dpMain, nextMain); } @@ -1091,20 +1097,20 @@ public class IndexPageCache * @param dpMain the index page * @param peerMain the peer index page */ - private static void validatePeerStatus(DataPageMain dpMain, DataPageMain peerMain) + private void validatePeerStatus(DataPageMain dpMain, DataPageMain peerMain) throws IOException { if(dpMain._leaf != peerMain._leaf) { - throw new IllegalStateException("Mismatched peer status " + - dpMain._leaf + " " + peerMain._leaf); + throw new IllegalStateException(withErrorContext( + "Mismatched peer status " + dpMain._leaf + " " + peerMain._leaf)); } if(!dpMain._leaf) { if((dpMain._parentPageNumber != null) && (peerMain._parentPageNumber != null) && ((int)dpMain._parentPageNumber != (int)peerMain._parentPageNumber)) { - throw new IllegalStateException("Mismatched node parents " + - dpMain._parentPageNumber + " " + - peerMain._parentPageNumber); + throw new IllegalStateException(withErrorContext( + "Mismatched node parents " + dpMain._parentPageNumber + " " + + peerMain._parentPageNumber)); } } } @@ -1112,7 +1118,7 @@ public class IndexPageCache /** * Collects all the cache pages in the cache. * - * @param paages the List to update + * @param pages the List to update * @param dpMain the index page to collect */ private List collectPages(List pages, DataPageMain dpMain) { @@ -1160,6 +1166,11 @@ public class IndexPageCache return sb.toString(); } + private String withErrorContext(String msg) { + return _indexData.withErrorContext(msg); + } + + /** * Keeps track of the main info for an index page. */ @@ -1193,11 +1204,11 @@ public class IndexPageCache } public boolean hasChildTail() { - return((int)_childTailPageNumber != INVALID_INDEX_PAGE_NUMBER); + return(_childTailPageNumber != INVALID_INDEX_PAGE_NUMBER); } public boolean isChildTailPageNumber(int pageNumber) { - return((int)_childTailPageNumber == pageNumber); + return(_childTailPageNumber == pageNumber); } public DataPageMain getParentPage() throws IOException @@ -1278,7 +1289,8 @@ public class IndexPageCache // pages along the path findCacheDataPage(getExtra()._entryView.getLast()); if(_parentPageNumber == null) { - throw new IllegalStateException("Parent was not resolved"); + throw new IllegalStateException(withErrorContext( + "Parent was not resolved")); } } } diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/LongValueColumnImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/LongValueColumnImpl.java index 89accff..2b54ee3 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/LongValueColumnImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/LongValueColumnImpl.java @@ -98,8 +98,8 @@ class LongValueColumnImpl extends ColumnImpl } return null; default: - throw new RuntimeException("unexpected var length, long value type: " + - getType()); + throw new RuntimeException(withErrorContext( + "unexpected var length, long value type: " + getType())); } } @@ -116,8 +116,8 @@ class LongValueColumnImpl extends ColumnImpl obj = encodeTextValue(obj, 0, getMaxLengthInUnits(), false).array(); break; default: - throw new RuntimeException("unexpected var length, long value type: " + - getType()); + throw new RuntimeException(withErrorContext( + "unexpected var length, long value type: " + getType())); } // create long value buffer @@ -147,8 +147,9 @@ class LongValueColumnImpl extends ColumnImpl int rowLen = def.remaining(); if(rowLen < length) { // warn the caller, but return whatever we can - LOG.warn(getName() + " value may be truncated: expected length " + - length + " found " + rowLen); + LOG.warn(withErrorContext( + "Value may be truncated: expected length " + + length + " found " + rowLen)); rtn = new byte[rowLen]; } @@ -158,9 +159,10 @@ class LongValueColumnImpl extends ColumnImpl // long value on other page(s) if (lvalDefinition.length != getFormat().SIZE_LONG_VALUE_DEF) { - throw new IOException("Expected " + getFormat().SIZE_LONG_VALUE_DEF + - " bytes in long value definition, but found " + - lvalDefinition.length); + throw new IOException(withErrorContext( + "Expected " + getFormat().SIZE_LONG_VALUE_DEF + + " bytes in long value definition, but found " + + lvalDefinition.length)); } int rowNum = ByteUtil.getUnsignedByte(def); @@ -178,8 +180,9 @@ class LongValueColumnImpl extends ColumnImpl int rowLen = rowEnd - rowStart; if(rowLen < length) { // warn the caller, but return whatever we can - LOG.warn(getName() + " value may be truncated: expected length " + - length + " found " + rowLen); + LOG.warn(withErrorContext( + "Value may be truncated: expected length " + + length + " found " + rowLen)); rtn = new byte[rowLen]; } @@ -219,7 +222,8 @@ class LongValueColumnImpl extends ColumnImpl break; default: - throw new IOException("Unrecognized long value type: " + type); + throw new IOException(withErrorContext( + "Unrecognized long value type: " + type)); } } @@ -255,9 +259,9 @@ class LongValueColumnImpl extends ColumnImpl throws IOException { if(value.length > getType().getMaxSize()) { - throw new IOException("value too big for column, max " + - getType().getMaxSize() + ", got " + - value.length); + throw new IOException(withErrorContext( + "value too big for column, max " + + getType().getMaxSize() + ", got " + value.length)); } // determine which type to write @@ -357,7 +361,8 @@ class LongValueColumnImpl extends ColumnImpl break; default: - throw new IOException("Unrecognized long value type: " + type); + throw new IOException(withErrorContext( + "Unrecognized long value type: " + type)); } // update def diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java index c720654..8d2dddb 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java @@ -65,6 +65,14 @@ class TableCreator Collections.emptyList()); } + public String getName() { + return _name; + } + + public DatabaseImpl getDatabase() { + return _database; + } + public JetFormat getFormat() { return _database.getFormat(); } diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java index a238864..d64e4fd 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableImpl.java @@ -279,8 +279,8 @@ public class TableImpl implements Table colOwnedPages = null; colFreeSpacePages = null; tableBuffer.position(pos + 8); - LOG.warn("Table " + _name + " invalid column " + umapColNum + - " usage map definition: " + e); + LOG.warn(withErrorContext("Invalid column " + umapColNum + + " usage map definition: " + e)); } for(ColumnImpl col : _columns) { @@ -426,8 +426,8 @@ public class TableImpl implements Table return column; } } - throw new IllegalArgumentException("Column with name " + name + - " does not exist in this table"); + throw new IllegalArgumentException(withErrorContext( + "Column with name " + name + " does not exist in this table")); } public boolean hasColumn(String name) { @@ -468,8 +468,8 @@ public class TableImpl implements Table return index; } } - throw new IllegalArgumentException("Index with name " + name + - " does not exist on this table"); + throw new IllegalArgumentException(withErrorContext( + "Index with name " + name + " does not exist on this table")); } public IndexImpl getPrimaryKeyIndex() { @@ -478,8 +478,8 @@ public class TableImpl implements Table return index; } } - throw new IllegalArgumentException("Table " + getName() + - " does not have a primary key index"); + throw new IllegalArgumentException(withErrorContext( + "No primary key index found")); } public IndexImpl getForeignKeyIndex(Table otherTable) { @@ -490,9 +490,9 @@ public class TableImpl implements Table return index; } } - throw new IllegalArgumentException( - "Table " + getName() + " does not have a foreign key reference to " + - otherTable.getName()); + throw new IllegalArgumentException(withErrorContext( + "No foreign key reference to " + + otherTable.getName() + " found")); } /** @@ -622,8 +622,8 @@ public class TableImpl implements Table throws IOException { if(this != column.getTable()) { - throw new IllegalArgumentException( - "Given column " + column + " is not from this table"); + throw new IllegalArgumentException(withErrorContext( + "Given column " + column + " is not from this table")); } requireValidRowId(rowId); @@ -933,7 +933,8 @@ public class TableImpl implements Table if (overflowRow) { if((rowEnd - rowStart) < 4) { - throw new IOException("invalid overflow row info"); + throw new IOException(rowState.getTable().withErrorContext( + "invalid overflow row info")); } // Overflow page. the "row" data in the current page points to @@ -1568,7 +1569,8 @@ public class TableImpl implements Table int rowSize = rowData.remaining(); if (rowSize > getFormat().MAX_ROW_SIZE) { - throw new IOException("Row size " + rowSize + " is too large"); + throw new IOException(withErrorContext( + "Row size " + rowSize + " is too large")); } // get page with space @@ -1662,14 +1664,17 @@ public class TableImpl implements Table // corruption (failed write vs. a row failure which was not a // write failure). we don't know the status of any rows at this // point (and the original failure is probably irrelevant) - LOG.warn("Secondary row failure which preceded the write failure", + LOG.warn(withErrorContext( + "Secondary row failure which preceded the write failure"), rowWriteFailure); updateCount = 0; rowWriteFailure = flushFailure; } } - throw new BatchUpdateException(updateCount, rowWriteFailure); + throw new BatchUpdateException( + updateCount, withErrorContext("Failed adding rows"), + rowWriteFailure); } } finally { @@ -1809,8 +1814,8 @@ public class TableImpl implements Table keepRawVarValues); if (newRowData.limit() > getFormat().MAX_ROW_SIZE) { - throw new IOException("Row size " + newRowData.limit() + - " is too large"); + throw new IOException(withErrorContext( + "Row size " + newRowData.limit() + " is too large")); } if(!_indexDatas.isEmpty()) { @@ -2364,23 +2369,25 @@ public class TableImpl implements Table /** * @throws IllegalStateException if the given rowId is invalid */ - private static void requireValidRowId(RowIdImpl rowId) { + private void requireValidRowId(RowIdImpl rowId) { if(!rowId.isValid()) { - throw new IllegalArgumentException("Given rowId is invalid: " + rowId); + throw new IllegalArgumentException(withErrorContext( + "Given rowId is invalid: " + rowId)); } } /** * @throws IllegalStateException if the given row is invalid or deleted */ - private static void requireNonDeletedRow(RowState rowState, RowIdImpl rowId) + private void requireNonDeletedRow(RowState rowState, RowIdImpl rowId) { if(!rowState.isValid()) { - throw new IllegalArgumentException( - "Given rowId is invalid for this table: " + rowId); + throw new IllegalArgumentException(withErrorContext( + "Given rowId is invalid for this table: " + rowId)); } if(rowState.isDeleted()) { - throw new IllegalStateException("Row is deleted: " + rowId); + throw new IllegalStateException(withErrorContext( + "Row is deleted: " + rowId)); } } @@ -2485,6 +2492,15 @@ public class TableImpl implements Table return copy; } + private String withErrorContext(String msg) { + return withErrorContext(msg, getDatabase(), getName()); + } + + private static String withErrorContext(String msg, DatabaseImpl db, + String tableName) { + return msg + " (Db=" + db.getName() + ";Table=" + tableName + ")"; + } + /** various statuses for the row data */ private enum RowStatus { INIT, INVALID_PAGE, INVALID_ROW, VALID, DELETED, NORMAL, OVERFLOW; @@ -2722,10 +2738,12 @@ public class TableImpl implements Table // this should never see modifications because it only happens within // the positionAtRowData method if(!isUpToDate()) { - throw new IllegalStateException("Table modified while searching?"); + throw new IllegalStateException(getTable().withErrorContext( + "Table modified while searching?")); } if(_rowStatus != RowStatus.OVERFLOW) { - throw new IllegalStateException("Row is not an overflow row?"); + throw new IllegalStateException(getTable().withErrorContext( + "Row is not an overflow row?")); } _finalRowId = rowId; _finalRowBuffer = _overflowRowBufferH.setPage(getPageChannel(), diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/query/QueryImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/query/QueryImpl.java index a695032..6007a16 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/query/QueryImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/query/QueryImpl.java @@ -78,7 +78,8 @@ public abstract class QueryImpl implements Query short foundType = getShortValue(getQueryType(rows), _type.getValue()); if(foundType != _type.getValue()) { - throw new IllegalStateException("Unexpected query type " + foundType); + throw new IllegalStateException(withErrorContext( + "Unexpected query type " + foundType)); } } } @@ -189,7 +190,8 @@ public abstract class QueryImpl implements Query @Override protected void format(StringBuilder builder, Row row) { String typeName = PARAM_TYPE_MAP.get(row.flag); if(typeName == null) { - throw new IllegalStateException("Unknown param type " + row.flag); + throw new IllegalStateException(withErrorContext( + "Unknown param type " + row.flag)); } builder.append(row.name1).append(' ').append(typeName); @@ -255,7 +257,8 @@ public abstract class QueryImpl implements Query Join toExpr = getJoinExpr(toTable, joinExprs); String joinType = JOIN_TYPE_MAP.get(join.flag); if(joinType == null) { - throw new IllegalStateException("Unknown join type " + join.flag); + throw new IllegalStateException(withErrorContext( + "Unknown join type " + join.flag)); } String expr = new StringBuilder().append(fromExpr) @@ -275,7 +278,7 @@ public abstract class QueryImpl implements Query return result; } - private static Join getJoinExpr(String table, List joinExprs) + private Join getJoinExpr(String table, List joinExprs) { for(Iterator iter = joinExprs.iterator(); iter.hasNext(); ) { Join joinExpr = iter.next(); @@ -284,10 +287,11 @@ public abstract class QueryImpl implements Query return joinExpr; } } - throw new IllegalStateException("Cannot find join table " + table); + throw new IllegalStateException(withErrorContext( + "Cannot find join table " + table)); } - private static Collection> combineJoins(List joins) + private Collection> combineJoins(List joins) { // combine joins with the same to/from tables Map,List> comboJoinMap = @@ -299,9 +303,9 @@ public abstract class QueryImpl implements Query comboJoins = new ArrayList(); comboJoinMap.put(key, comboJoins); } else { - if((short)comboJoins.get(0).flag != join.flag) { - throw new IllegalStateException( - "Mismatched join flags for combo joins"); + if(comboJoins.get(0).flag != join.flag) { + throw new IllegalStateException(withErrorContext( + "Mismatched join flags for combo joins")); } } comboJoins.add(join); @@ -417,18 +421,18 @@ public abstract class QueryImpl implements Query return new UnionQueryImpl(name, rows, objectId, objectFlag); default: // unknown querytype - throw new IllegalStateException( - "unknown query object flag " + typeFlag); + throw new IllegalStateException(withErrorContext( + "unknown query object flag " + typeFlag, name)); } } catch(IllegalStateException e) { - LOG.warn("Failed parsing query", e); + LOG.warn(withErrorContext("Failed parsing query", name), e); } // return unknown query return new UnknownQueryImpl(name, rows, objectId, objectFlag); } - private static Short getQueryType(List rows) + private Short getQueryType(List rows) { return getUniqueRow(getRowsByAttribute(rows, TYPE_ATTRIBUTE)).flag; } @@ -443,14 +447,15 @@ public abstract class QueryImpl implements Query return result; } - protected static Row getUniqueRow(List rows) { + protected Row getUniqueRow(List rows) { if(rows.size() == 1) { return rows.get(0); } if(rows.isEmpty()) { return EMPTY_ROW; } - throw new IllegalStateException("Unexpected number of rows for" + rows); + throw new IllegalStateException(withErrorContext( + "Unexpected number of rows for" + rows)); } protected static List filterRowsByFlag( @@ -545,6 +550,15 @@ public abstract class QueryImpl implements Query return builder; } + private String withErrorContext(String msg) { + return withErrorContext(msg, getName()); + } + + private static String withErrorContext(String msg, String queryName) { + return msg + " (Query: " + queryName + ")"; + } + + private static final class UnknownQueryImpl extends QueryImpl { private UnknownQueryImpl(String name, List rows, int objectId, diff --git a/src/main/java/com/healthmarketscience/jackcess/util/Joiner.java b/src/main/java/com/healthmarketscience/jackcess/util/Joiner.java index 65f6441..dfd0ef4 100644 --- a/src/main/java/com/healthmarketscience/jackcess/util/Joiner.java +++ b/src/main/java/com/healthmarketscience/jackcess/util/Joiner.java @@ -31,6 +31,7 @@ import com.healthmarketscience.jackcess.Index; import com.healthmarketscience.jackcess.IndexCursor; import com.healthmarketscience.jackcess.Row; import com.healthmarketscience.jackcess.Table; +import com.healthmarketscience.jackcess.impl.DatabaseImpl; import com.healthmarketscience.jackcess.impl.IndexImpl; /** @@ -291,7 +292,10 @@ public class Joiner for(int i = 1; i < toCols.size(); ++i) { sb.append(",").append(toCols.get(i).getName()); } - sb.append(toType); + sb.append(toType) + .append(" (Db=") + .append(((DatabaseImpl)getFromTable().getDatabase()).getName()) + .append(")"); return sb.toString(); } -- 2.39.5