git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@941 f203690c-595d-4dc9-a70b-905162fa7fd2tags/jackcess-2.1.1
Load linked table info from system table when reading databases with | Load linked table info from system table when reading databases with | ||||
unsupported sort orders. | unsupported sort orders. | ||||
</action> | </action> | ||||
<action dev="jahlborn" type="update" system="SourceForge2Features" | |||||
issue="32"> | |||||
Allow optional direct insert/update of autonumber values. This is | |||||
disabled by default, but can be selectively enabled per-jvm (using | |||||
system property), per-database, and per-table. | |||||
</action> | |||||
</release> | </release> | ||||
<release version="2.1.0" date="2015-04-16" | <release version="2.1.0" date="2015-04-16" | ||||
description="Relicense to Apache License"> | description="Relicense to Apache License"> |
public static final String FK_ENFORCE_PROPERTY = | public static final String FK_ENFORCE_PROPERTY = | ||||
"com.healthmarketscience.jackcess.enforceForeignKeys"; | "com.healthmarketscience.jackcess.enforceForeignKeys"; | ||||
/** system property which can be used to set the default allow auto number | |||||
* insert policy. Defaults to {@code false}. | |||||
* @usage _general_field_ | |||||
*/ | |||||
public static final String ALLOW_AUTONUM_INSERT_PROPERTY = | |||||
"com.healthmarketscience.jackcess.allowAutoNumberInsert"; | |||||
/** | /** | ||||
* Enum which indicates which version of Access created the database. | * Enum which indicates which version of Access created the database. | ||||
* @usage _general_class_ | * @usage _general_class_ | ||||
*/ | */ | ||||
public void setEnforceForeignKeys(Boolean newEnforceForeignKeys); | public void setEnforceForeignKeys(Boolean newEnforceForeignKeys); | ||||
/** | |||||
* Gets current allow auto number insert policy. By default, jackcess does | |||||
* not allow auto numbers to be inserted or updated directly (they are | |||||
* always handled internally by the Table). Setting this policy to {@code | |||||
* true} allows the caller to optionally set the value explicitly when | |||||
* adding or updating rows (if a value is not provided, it will still be | |||||
* handled internally by the Table). This value can be set database-wide | |||||
* using {@link #setAllowAutoNumberInsert} and/or on a per-table basis using | |||||
* {@link Table#setAllowAutoNumberInsert} (and/or on a jvm-wide using the | |||||
* {@link #ALLOW_AUTONUM_INSERT_PROPERTY} system property). Note that | |||||
* <i>enabling this feature should be done with care</i> to reduce the | |||||
* chances of screwing up the database. | |||||
* | |||||
* @usage _intermediate_method_ | |||||
*/ | |||||
public boolean isAllowAutoNumberInsert(); | |||||
/** | |||||
* Sets the new auto number insert policy for the database (unless | |||||
* overridden at the Table level). If {@code null}, resets to the default | |||||
* value. | |||||
* @usage _intermediate_method_ | |||||
*/ | |||||
public void setAllowAutoNumberInsert(Boolean allowAutoNumInsert); | |||||
/** | /** | ||||
* Gets currently configured ColumnValidatorFactory (always non-{@code null}). | * Gets currently configured ColumnValidatorFactory (always non-{@code null}). | ||||
* @usage _intermediate_method_ | * @usage _intermediate_method_ |
*/ | */ | ||||
public void setErrorHandler(ErrorHandler newErrorHandler); | public void setErrorHandler(ErrorHandler newErrorHandler); | ||||
/** | |||||
* Gets the currently configured auto number insert policy. | |||||
* @see Database#isAllowAutoNumberInsert | |||||
* @usage _intermediate_method_ | |||||
*/ | |||||
public boolean isAllowAutoNumberInsert(); | |||||
/** | |||||
* Sets the new auto number insert policy for the Table. If {@code null}, | |||||
* resets to using the policy configured at the Database level. | |||||
* @usage _intermediate_method_ | |||||
*/ | |||||
public void setAllowAutoNumberInsert(Boolean allowAutoNumInsert); | |||||
/** | /** | ||||
* @return All of the columns in this table (unmodifiable List) | * @return All of the columns in this table (unmodifiable List) | ||||
* @usage _general_method_ | * @usage _general_method_ |
private static final char MIN_COMPRESS_CHAR = 1; | private static final char MIN_COMPRESS_CHAR = 1; | ||||
private static final char MAX_COMPRESS_CHAR = 0xFF; | private static final char MAX_COMPRESS_CHAR = 0xFF; | ||||
/** auto numbers must be > 0 */ | |||||
static final int INVALID_AUTO_NUMBER = 0; | |||||
/** owning table */ | /** owning table */ | ||||
private final TableImpl _table; | private final TableImpl _table; | ||||
/** Whether or not the column is of variable length */ | /** Whether or not the column is of variable length */ | ||||
* <i>Warning, calling this externally will result in this value being | * <i>Warning, calling this externally will result in this value being | ||||
* "lost" for the table.</i> | * "lost" for the table.</i> | ||||
*/ | */ | ||||
public abstract Object getNext(Object prevRowValue); | |||||
public abstract Object getNext(TableImpl.WriteRowState writeRowState); | |||||
/** | |||||
* Returns a valid autonumber for this generator. | |||||
* <p> | |||||
* <i>Warning, calling this externally may result in this value being | |||||
* "lost" for the table.</i> | |||||
*/ | |||||
public abstract Object handleInsert( | |||||
TableImpl.WriteRowState writeRowState, Object inRowValue) | |||||
throws IOException; | |||||
/** | /** | ||||
* Restores a previous autonumber generated by this generator. | * Restores a previous autonumber generated by this generator. | ||||
} | } | ||||
@Override | @Override | ||||
public Object getNext(Object prevRowValue) { | |||||
public Object getNext(TableImpl.WriteRowState writeRowState) { | |||||
// the table stores the last long autonumber used | // the table stores the last long autonumber used | ||||
return getTable().getNextLongAutoNumber(); | return getTable().getNextLongAutoNumber(); | ||||
} | } | ||||
@Override | |||||
public Object handleInsert(TableImpl.WriteRowState writeRowState, | |||||
Object inRowValue) | |||||
throws IOException | |||||
{ | |||||
int inAutoNum = toNumber(inRowValue).intValue(); | |||||
if(inAutoNum <= INVALID_AUTO_NUMBER) { | |||||
throw new IOException(withErrorContext( | |||||
"Invalid auto number value " + inAutoNum)); | |||||
} | |||||
// the table stores the last long autonumber used | |||||
getTable().adjustLongAutoNumber(inAutoNum); | |||||
return inAutoNum; | |||||
} | |||||
@Override | @Override | ||||
public void restoreLast(Object last) { | public void restoreLast(Object last) { | ||||
if(last instanceof Integer) { | if(last instanceof Integer) { | ||||
} | } | ||||
@Override | @Override | ||||
public Object getNext(Object prevRowValue) { | |||||
public Object getNext(TableImpl.WriteRowState writeRowState) { | |||||
// format guids consistently w/ Column.readGUIDValue() | // format guids consistently w/ Column.readGUIDValue() | ||||
_lastAutoNumber = "{" + UUID.randomUUID() + "}"; | _lastAutoNumber = "{" + UUID.randomUUID() + "}"; | ||||
return _lastAutoNumber; | return _lastAutoNumber; | ||||
} | } | ||||
@Override | |||||
public Object handleInsert(TableImpl.WriteRowState writeRowState, | |||||
Object inRowValue) | |||||
throws IOException | |||||
{ | |||||
_lastAutoNumber = toCharSequence(inRowValue); | |||||
return _lastAutoNumber; | |||||
} | |||||
@Override | @Override | ||||
public void restoreLast(Object last) { | public void restoreLast(Object last) { | ||||
_lastAutoNumber = null; | _lastAutoNumber = null; | ||||
} | } | ||||
@Override | @Override | ||||
public Object getNext(Object prevRowValue) { | |||||
int nextComplexAutoNum = | |||||
((prevRowValue == null) ? | |||||
// the table stores the last ComplexType autonumber used | |||||
getTable().getNextComplexTypeAutoNumber() : | |||||
// same value is shared across all ComplexType values in a row | |||||
((ComplexValueForeignKey)prevRowValue).get()); | |||||
return new ComplexValueForeignKeyImpl(ColumnImpl.this, nextComplexAutoNum); | |||||
public Object getNext(TableImpl.WriteRowState writeRowState) { | |||||
// same value is shared across all ComplexType values in a row | |||||
int nextComplexAutoNum = writeRowState.getComplexAutoNumber(); | |||||
if(nextComplexAutoNum <= INVALID_AUTO_NUMBER) { | |||||
// the table stores the last ComplexType autonumber used | |||||
nextComplexAutoNum = getTable().getNextComplexTypeAutoNumber(); | |||||
writeRowState.setComplexAutoNumber(nextComplexAutoNum); | |||||
} | |||||
return new ComplexValueForeignKeyImpl(ColumnImpl.this, | |||||
nextComplexAutoNum); | |||||
} | |||||
@Override | |||||
public Object handleInsert(TableImpl.WriteRowState writeRowState, | |||||
Object inRowValue) | |||||
throws IOException | |||||
{ | |||||
ComplexValueForeignKey inComplexFK = null; | |||||
if(inRowValue instanceof ComplexValueForeignKey) { | |||||
inComplexFK = (ComplexValueForeignKey)inRowValue; | |||||
} else { | |||||
inComplexFK = new ComplexValueForeignKeyImpl( | |||||
ColumnImpl.this, toNumber(inRowValue).intValue()); | |||||
} | |||||
if(inComplexFK.getColumn() != ColumnImpl.this) { | |||||
throw new IOException(withErrorContext( | |||||
"Wrong column for complex value foreign key, found " + | |||||
inComplexFK.getColumn().getName())); | |||||
} | |||||
if(inComplexFK.get() < 1) { | |||||
throw new IOException(withErrorContext( | |||||
"Invalid complex value foreign key value " + inComplexFK.get())); | |||||
} | |||||
// same value is shared across all ComplexType values in a row | |||||
int prevRowValue = writeRowState.getComplexAutoNumber(); | |||||
if(prevRowValue <= INVALID_AUTO_NUMBER) { | |||||
writeRowState.setComplexAutoNumber(inComplexFK.get()); | |||||
} else if(prevRowValue != inComplexFK.get()) { | |||||
throw new IOException(withErrorContext( | |||||
"Inconsistent complex value foreign key values: found " + | |||||
prevRowValue + ", given " + inComplexFK)); | |||||
} | |||||
// the table stores the last ComplexType autonumber used | |||||
getTable().adjustComplexTypeAutoNumber(inComplexFK.get()); | |||||
return inComplexFK; | |||||
} | } | ||||
@Override | @Override | ||||
} | } | ||||
@Override | @Override | ||||
public Object getNext(Object prevRowValue) { | |||||
public Object getNext(TableImpl.WriteRowState writeRowState) { | |||||
throw new UnsupportedOperationException(); | |||||
} | |||||
@Override | |||||
public Object handleInsert(TableImpl.WriteRowState writeRowState, | |||||
Object inRowValue) { | |||||
throw new UnsupportedOperationException(); | throw new UnsupportedOperationException(); | ||||
} | } | ||||
private Table.ColumnOrder _columnOrder; | private Table.ColumnOrder _columnOrder; | ||||
/** whether or not enforcement of foreign-keys is enabled */ | /** whether or not enforcement of foreign-keys is enabled */ | ||||
private boolean _enforceForeignKeys; | private boolean _enforceForeignKeys; | ||||
/** whether or not auto numbers can be directly inserted by the user */ | |||||
private boolean _allowAutoNumInsert; | |||||
/** factory for ColumnValidators */ | /** factory for ColumnValidators */ | ||||
private ColumnValidatorFactory _validatorFactory = SimpleColumnValidatorFactory.INSTANCE; | private ColumnValidatorFactory _validatorFactory = SimpleColumnValidatorFactory.INSTANCE; | ||||
/** cache of in-use tables */ | /** cache of in-use tables */ | ||||
_charset = ((charset == null) ? getDefaultCharset(_format) : charset); | _charset = ((charset == null) ? getDefaultCharset(_format) : charset); | ||||
_columnOrder = getDefaultColumnOrder(); | _columnOrder = getDefaultColumnOrder(); | ||||
_enforceForeignKeys = getDefaultEnforceForeignKeys(); | _enforceForeignKeys = getDefaultEnforceForeignKeys(); | ||||
_allowAutoNumInsert = getDefaultAllowAutoNumberInsert(); | |||||
_fileFormat = fileFormat; | _fileFormat = fileFormat; | ||||
_pageChannel = new PageChannel(channel, closeChannel, _format, autoSync); | _pageChannel = new PageChannel(channel, closeChannel, _format, autoSync); | ||||
_timeZone = ((timeZone == null) ? getDefaultTimeZone() : timeZone); | _timeZone = ((timeZone == null) ? getDefaultTimeZone() : timeZone); | ||||
_enforceForeignKeys = newEnforceForeignKeys; | _enforceForeignKeys = newEnforceForeignKeys; | ||||
} | } | ||||
public boolean isAllowAutoNumberInsert() { | |||||
return _allowAutoNumInsert; | |||||
} | |||||
public void setAllowAutoNumberInsert(Boolean allowAutoNumInsert) { | |||||
if(allowAutoNumInsert == null) { | |||||
allowAutoNumInsert = getDefaultAllowAutoNumberInsert(); | |||||
} | |||||
_allowAutoNumInsert = allowAutoNumInsert; | |||||
} | |||||
public ColumnValidatorFactory getColumnValidatorFactory() { | public ColumnValidatorFactory getColumnValidatorFactory() { | ||||
return _validatorFactory; | return _validatorFactory; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
/** | |||||
* Returns the default allow auto number insert policy. This defaults to | |||||
* {@code false}, but can be overridden using the system | |||||
* property {@value com.healthmarketscience.jackcess.Database#ALLOW_AUTONUM_INSERT_PROPERTY}. | |||||
* @usage _advanced_method_ | |||||
*/ | |||||
public static boolean getDefaultAllowAutoNumberInsert() | |||||
{ | |||||
String prop = System.getProperty(ALLOW_AUTONUM_INSERT_PROPERTY); | |||||
if(prop != null) { | |||||
return Boolean.TRUE.toString().equalsIgnoreCase(prop); | |||||
} | |||||
return false; | |||||
} | |||||
/** | /** | ||||
* Copies the given InputStream to the given channel using the most | * Copies the given InputStream to the given channel using the most | ||||
* efficient means possible. | * efficient means possible. |
import com.healthmarketscience.jackcess.ColumnBuilder; | import com.healthmarketscience.jackcess.ColumnBuilder; | ||||
import com.healthmarketscience.jackcess.ConstraintViolationException; | import com.healthmarketscience.jackcess.ConstraintViolationException; | ||||
import com.healthmarketscience.jackcess.CursorBuilder; | import com.healthmarketscience.jackcess.CursorBuilder; | ||||
import com.healthmarketscience.jackcess.DataType; | |||||
import com.healthmarketscience.jackcess.IndexBuilder; | import com.healthmarketscience.jackcess.IndexBuilder; | ||||
import com.healthmarketscience.jackcess.JackcessException; | import com.healthmarketscience.jackcess.JackcessException; | ||||
import com.healthmarketscience.jackcess.PropertyMap; | import com.healthmarketscience.jackcess.PropertyMap; | ||||
private PropertyMap _props; | private PropertyMap _props; | ||||
/** properties group for this table (and columns) */ | /** properties group for this table (and columns) */ | ||||
private PropertyMaps _propertyMaps; | private PropertyMaps _propertyMaps; | ||||
/** optional flag indicating whether or not auto numbers can be directly | |||||
inserted by the user */ | |||||
private Boolean _allowAutoNumInsert; | |||||
/** foreign-key enforcer for this table */ | /** foreign-key enforcer for this table */ | ||||
private final FKEnforcer _fkEnforcer; | private final FKEnforcer _fkEnforcer; | ||||
return _tableDefPageNumber; | return _tableDefPageNumber; | ||||
} | } | ||||
public boolean isAllowAutoNumberInsert() { | |||||
return ((_allowAutoNumInsert != null) ? (boolean)_allowAutoNumInsert : | |||||
getDatabase().isAllowAutoNumberInsert()); | |||||
} | |||||
public void setAllowAutoNumberInsert(Boolean allowAutoNumInsert) { | |||||
_allowAutoNumInsert = allowAutoNumInsert; | |||||
} | |||||
/** | /** | ||||
* @usage _advanced_method_ | * @usage _advanced_method_ | ||||
*/ | */ | ||||
/** | /** | ||||
* @param buffer Buffer to write to | * @param buffer Buffer to write to | ||||
* @param columns List of Columns in the table | |||||
*/ | */ | ||||
private static void writeTableDefinitionHeader( | private static void writeTableDefinitionHeader( | ||||
TableCreator creator, ByteBuffer buffer, int totalTableDefSize) | TableCreator creator, ByteBuffer buffer, int totalTableDefSize) | ||||
/** | /** | ||||
* Add multiple rows to this table, only writing to disk after all | * Add multiple rows to this table, only writing to disk after all | ||||
* rows have been written, and every time a data page is filled. | * rows have been written, and every time a data page is filled. | ||||
* @param inRows List of Object[] row values | |||||
* @param rows List of Object[] row values | |||||
*/ | */ | ||||
private List<? extends Object[]> addRows(List<? extends Object[]> rows, | private List<? extends Object[]> addRows(List<? extends Object[]> rows, | ||||
final boolean isBatchWrite) | final boolean isBatchWrite) | ||||
int pageNumber = PageChannel.INVALID_PAGE_NUMBER; | int pageNumber = PageChannel.INVALID_PAGE_NUMBER; | ||||
int updateCount = 0; | int updateCount = 0; | ||||
int autoNumAssignCount = 0; | int autoNumAssignCount = 0; | ||||
WriteRowState writeRowState = | |||||
(!_autoNumColumns.isEmpty() ? new WriteRowState() : null); | |||||
try { | try { | ||||
List<Object[]> dupeRows = null; | List<Object[]> dupeRows = null; | ||||
} | } | ||||
// fill in autonumbers | // fill in autonumbers | ||||
handleAutoNumbersForAdd(row); | |||||
handleAutoNumbersForAdd(row, writeRowState); | |||||
++autoNumAssignCount; | ++autoNumAssignCount; | ||||
// write the row of data to a temporary buffer | // write the row of data to a temporary buffer | ||||
// handle various value massaging activities | // handle various value massaging activities | ||||
for(ColumnImpl column : _columns) { | for(ColumnImpl column : _columns) { | ||||
Object rowValue = null; | |||||
if(column.isAutoNumber()) { | if(column.isAutoNumber()) { | ||||
// fill in any auto-numbers (we don't allow autonumber values to be | |||||
// modified) | |||||
rowValue = getRowColumn(getFormat(), rowBuffer, column, rowState, null); | |||||
} else { | |||||
// handle these separately (below) | |||||
continue; | |||||
} | |||||
rowValue = column.getRowValue(row); | |||||
if(rowValue == Column.KEEP_VALUE) { | |||||
Object rowValue = column.getRowValue(row); | |||||
if(rowValue == Column.KEEP_VALUE) { | |||||
// fill in any "keep value" fields (restore old value) | |||||
rowValue = getRowColumn(getFormat(), rowBuffer, column, rowState, | |||||
keepRawVarValues); | |||||
// fill in any "keep value" fields (restore old value) | |||||
rowValue = getRowColumn(getFormat(), rowBuffer, column, rowState, | |||||
keepRawVarValues); | |||||
} else { | |||||
// set oldValue to something that could not possibly be a real value | |||||
Object oldValue = Column.KEEP_VALUE; | |||||
if(_indexColumns.contains(column)) { | |||||
// read (old) row value to help update indexes | |||||
oldValue = getRowColumn(getFormat(), rowBuffer, column, rowState, null); | |||||
} else { | |||||
oldValue = rowState.getRowCacheValue(column.getColumnIndex()); | |||||
} | |||||
} else { | |||||
// if the old value was passed back in, we don't need to validate | |||||
if(oldValue != rowValue) { | |||||
// pass input value through column validator | |||||
rowValue = column.validate(rowValue); | |||||
} | |||||
// set oldValue to something that could not possibly be a real value | |||||
Object oldValue = Column.KEEP_VALUE; | |||||
if(_indexColumns.contains(column)) { | |||||
// read (old) row value to help update indexes | |||||
oldValue = getRowColumn(getFormat(), rowBuffer, column, rowState, | |||||
null); | |||||
} else { | |||||
oldValue = rowState.getRowCacheValue(column.getColumnIndex()); | |||||
} | } | ||||
// if the old value was passed back in, we don't need to validate | |||||
if(oldValue != rowValue) { | |||||
// pass input value through column validator | |||||
rowValue = column.validate(rowValue); | |||||
} | |||||
} | } | ||||
column.setRowValue(row, rowValue); | column.setRowValue(row, rowValue); | ||||
} | } | ||||
// fill in autonumbers | |||||
handleAutoNumbersForUpdate(row, rowBuffer, rowState); | |||||
// generate new row bytes | // generate new row bytes | ||||
ByteBuffer newRowData = createRow( | ByteBuffer newRowData = createRow( | ||||
row, _writeRowBufferH.getPageBuffer(getPageChannel()), oldRowSize, | row, _writeRowBufferH.getPageBuffer(getPageChannel()), oldRowSize, | ||||
} | } | ||||
/** | /** | ||||
* Fill in all autonumber column values. | |||||
* Fill in all autonumber column values for add. | |||||
*/ | */ | ||||
private void handleAutoNumbersForAdd(Object[] row) | |||||
private void handleAutoNumbersForAdd(Object[] row, WriteRowState writeRowState) | |||||
throws IOException | throws IOException | ||||
{ | { | ||||
if(_autoNumColumns.isEmpty()) { | if(_autoNumColumns.isEmpty()) { | ||||
return; | return; | ||||
} | } | ||||
Object complexAutoNumber = null; | |||||
boolean enableInsert = isAllowAutoNumberInsert(); | |||||
writeRowState.resetAutoNumber(); | |||||
for(ColumnImpl col : _autoNumColumns) { | for(ColumnImpl col : _autoNumColumns) { | ||||
// ignore given row value, use next autonumber | |||||
// ignore input row value, use original row value (unless explicitly | |||||
// enabled) | |||||
Object inRowValue = getInputAutoNumberRowValue(enableInsert, col, row); | |||||
ColumnImpl.AutoNumberGenerator autoNumGen = col.getAutoNumberGenerator(); | ColumnImpl.AutoNumberGenerator autoNumGen = col.getAutoNumberGenerator(); | ||||
Object rowValue = null; | |||||
if(autoNumGen.getType() != DataType.COMPLEX_TYPE) { | |||||
rowValue = autoNumGen.getNext(null); | |||||
} else { | |||||
// complex type auto numbers are shared across all complex columns | |||||
// in the row | |||||
complexAutoNumber = autoNumGen.getNext(complexAutoNumber); | |||||
rowValue = complexAutoNumber; | |||||
} | |||||
Object rowValue = ((inRowValue == null) ? | |||||
autoNumGen.getNext(writeRowState) : | |||||
autoNumGen.handleInsert(writeRowState, inRowValue)); | |||||
col.setRowValue(row, rowValue); | col.setRowValue(row, rowValue); | ||||
} | } | ||||
} | } | ||||
/** | |||||
* Fill in all autonumber column values for update. | |||||
*/ | |||||
private void handleAutoNumbersForUpdate(Object[] row, ByteBuffer rowBuffer, | |||||
RowState rowState) | |||||
throws IOException | |||||
{ | |||||
if(_autoNumColumns.isEmpty()) { | |||||
return; | |||||
} | |||||
boolean enableInsert = isAllowAutoNumberInsert(); | |||||
rowState.resetAutoNumber(); | |||||
for(ColumnImpl col : _autoNumColumns) { | |||||
// ignore input row value, use original row value (unless explicitly | |||||
// enabled) | |||||
Object inRowValue = getInputAutoNumberRowValue(enableInsert, col, row); | |||||
Object rowValue = | |||||
((inRowValue == null) ? | |||||
getRowColumn(getFormat(), rowBuffer, col, rowState, null) : | |||||
col.getAutoNumberGenerator().handleInsert(rowState, inRowValue)); | |||||
col.setRowValue(row, rowValue); | |||||
} | |||||
} | |||||
/** | |||||
* Optionally get the input autonumber row value for the given column from | |||||
* the given row if one was provided. | |||||
*/ | |||||
private static Object getInputAutoNumberRowValue( | |||||
boolean enableInsert, ColumnImpl col, Object[] row) | |||||
{ | |||||
if(!enableInsert) { | |||||
return null; | |||||
} | |||||
Object inRowValue = col.getRowValue(row); | |||||
if((inRowValue == Column.KEEP_VALUE) || (inRowValue == Column.AUTO_NUMBER)) { | |||||
// these "special" values both behave like nothing was given | |||||
inRowValue = null; | |||||
} | |||||
return inRowValue; | |||||
} | |||||
/** | /** | ||||
* Restores all autonumber column values from a failed add row. | * Restores all autonumber column values from a failed add row. | ||||
*/ | */ | ||||
return _lastLongAutoNumber; | return _lastLongAutoNumber; | ||||
} | } | ||||
void adjustLongAutoNumber(int inLongAutoNumber) { | |||||
if(inLongAutoNumber > _lastLongAutoNumber) { | |||||
_lastLongAutoNumber = inLongAutoNumber; | |||||
} | |||||
} | |||||
void restoreLastLongAutoNumber(int lastLongAutoNumber) { | void restoreLastLongAutoNumber(int lastLongAutoNumber) { | ||||
// restores the last used auto number | // restores the last used auto number | ||||
_lastLongAutoNumber = lastLongAutoNumber - 1; | _lastLongAutoNumber = lastLongAutoNumber - 1; | ||||
return _lastComplexTypeAutoNumber; | return _lastComplexTypeAutoNumber; | ||||
} | } | ||||
void adjustComplexTypeAutoNumber(int inComplexTypeAutoNumber) { | |||||
if(inComplexTypeAutoNumber > _lastComplexTypeAutoNumber) { | |||||
_lastComplexTypeAutoNumber = inComplexTypeAutoNumber; | |||||
} | |||||
} | |||||
void restoreLastComplexTypeAutoNumber(int lastComplexTypeAutoNumber) { | void restoreLastComplexTypeAutoNumber(int lastComplexTypeAutoNumber) { | ||||
// restores the last used auto number | // restores the last used auto number | ||||
_lastComplexTypeAutoNumber = lastComplexTypeAutoNumber - 1; | _lastComplexTypeAutoNumber = lastComplexTypeAutoNumber - 1; | ||||
} | } | ||||
/** | /** | ||||
* Maintains the state of reading a row of data. | |||||
* Maintains state for writing a new row of data. | |||||
*/ | |||||
protected static class WriteRowState | |||||
{ | |||||
private int _complexAutoNumber = ColumnImpl.INVALID_AUTO_NUMBER; | |||||
public int getComplexAutoNumber() { | |||||
return _complexAutoNumber; | |||||
} | |||||
public void setComplexAutoNumber(int complexAutoNumber) { | |||||
_complexAutoNumber = complexAutoNumber; | |||||
} | |||||
public void resetAutoNumber() { | |||||
_complexAutoNumber = ColumnImpl.INVALID_AUTO_NUMBER; | |||||
} | |||||
} | |||||
/** | |||||
* Maintains the state of reading/updating a row of data. | |||||
* @usage _advanced_class_ | * @usage _advanced_class_ | ||||
*/ | */ | ||||
public final class RowState implements ErrorHandler.Location | |||||
public final class RowState extends WriteRowState | |||||
implements ErrorHandler.Location | |||||
{ | { | ||||
/** Buffer used for reading the header row data pages */ | /** Buffer used for reading the header row data pages */ | ||||
private final TempPageHolder _headerRowBufferH; | private final TempPageHolder _headerRowBufferH; | ||||
} | } | ||||
public void reset() { | public void reset() { | ||||
resetAutoNumber(); | |||||
_finalRowId = null; | _finalRowId = null; | ||||
_finalRowBuffer = null; | _finalRowBuffer = null; | ||||
_rowsOnHeaderPage = 0; | _rowsOnHeaderPage = 0; |
} | } | ||||
} | } | ||||
public void testAutoNumber() throws Exception { | |||||
for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) { | |||||
Database db = createMem(fileFormat); | |||||
Table table = new TableBuilder("test") | |||||
.addColumn(new ColumnBuilder("a", DataType.LONG) | |||||
.setAutoNumber(true)) | |||||
.addColumn(new ColumnBuilder("b", DataType.TEXT)) | |||||
.toTable(db); | |||||
doTestAutoNumber(table); | |||||
db.close(); | |||||
} | |||||
} | |||||
public void testAutoNumberPK() throws Exception { | |||||
for (final TestDB testDB : SUPPORTED_DBS_TEST) { | |||||
Database db = openMem(testDB); | |||||
Table table = db.getTable("Table3"); | |||||
doTestAutoNumber(table); | |||||
db.close(); | |||||
} | |||||
} | |||||
private void doTestAutoNumber(Table table) throws Exception | |||||
{ | |||||
Object[] row = {null, "row1"}; | |||||
assertSame(row, table.addRow(row)); | |||||
assertEquals(1, ((Integer)row[0]).intValue()); | |||||
row = table.addRow(13, "row2"); | |||||
assertEquals(2, ((Integer)row[0]).intValue()); | |||||
row = table.addRow("flubber", "row3"); | |||||
assertEquals(3, ((Integer)row[0]).intValue()); | |||||
table.reset(); | |||||
row = table.addRow(Column.AUTO_NUMBER, "row4"); | |||||
assertEquals(4, ((Integer)row[0]).intValue()); | |||||
row = table.addRow(Column.AUTO_NUMBER, "row5"); | |||||
assertEquals(5, ((Integer)row[0]).intValue()); | |||||
Object[] smallRow = {Column.AUTO_NUMBER}; | |||||
row = table.addRow(smallRow); | |||||
assertNotSame(row, smallRow); | |||||
assertEquals(6, ((Integer)row[0]).intValue()); | |||||
table.reset(); | |||||
List<? extends Map<String, Object>> expectedRows = | |||||
createExpectedTable( | |||||
createExpectedRow( | |||||
"a", 1, | |||||
"b", "row1"), | |||||
createExpectedRow( | |||||
"a", 2, | |||||
"b", "row2"), | |||||
createExpectedRow( | |||||
"a", 3, | |||||
"b", "row3"), | |||||
createExpectedRow( | |||||
"a", 4, | |||||
"b", "row4"), | |||||
createExpectedRow( | |||||
"a", 5, | |||||
"b", "row5"), | |||||
createExpectedRow( | |||||
"a", 6, | |||||
"b", null)); | |||||
assertTable(expectedRows, table); | |||||
} | |||||
public void testWriteAndReadDate() throws Exception { | public void testWriteAndReadDate() throws Exception { | ||||
for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) { | for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) { | ||||
Database db = createMem(fileFormat); | Database db = createMem(fileFormat); |
return Arrays.<Row>asList(rows); | return Arrays.<Row>asList(rows); | ||||
} | } | ||||
static void dumpDatabase(Database mdb) throws Exception { | |||||
public static void dumpDatabase(Database mdb) throws Exception { | |||||
dumpDatabase(mdb, false); | dumpDatabase(mdb, false); | ||||
} | } | ||||
static void dumpDatabase(Database mdb, boolean systemTables) | |||||
public static void dumpDatabase(Database mdb, boolean systemTables) | |||||
throws Exception | throws Exception | ||||
{ | { | ||||
dumpDatabase(mdb, systemTables, new PrintWriter(System.out, true)); | dumpDatabase(mdb, systemTables, new PrintWriter(System.out, true)); | ||||
} | } | ||||
static void dumpTable(Table table) throws Exception { | |||||
public static void dumpTable(Table table) throws Exception { | |||||
dumpTable(table, new PrintWriter(System.out, true)); | dumpTable(table, new PrintWriter(System.out, true)); | ||||
} | } | ||||
/* | |||||
Copyright (c) 2015 James Ahlborn | |||||
Licensed under the Apache License, Version 2.0 (the "License"); | |||||
you may not use this file except in compliance with the License. | |||||
You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
*/ | |||||
package com.healthmarketscience.jackcess.impl; | |||||
import java.io.IOException; | |||||
import java.util.Collections; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import java.util.UUID; | |||||
import com.healthmarketscience.jackcess.Column; | |||||
import com.healthmarketscience.jackcess.ColumnBuilder; | |||||
import com.healthmarketscience.jackcess.CursorBuilder; | |||||
import com.healthmarketscience.jackcess.DataType; | |||||
import com.healthmarketscience.jackcess.Database; | |||||
import static com.healthmarketscience.jackcess.Database.*; | |||||
import com.healthmarketscience.jackcess.Row; | |||||
import com.healthmarketscience.jackcess.Table; | |||||
import com.healthmarketscience.jackcess.TableBuilder; | |||||
import static com.healthmarketscience.jackcess.TestUtil.*; | |||||
import com.healthmarketscience.jackcess.complex.ComplexValueForeignKey; | |||||
import static com.healthmarketscience.jackcess.impl.JetFormatTest.*; | |||||
import junit.framework.TestCase; | |||||
/** | |||||
* | |||||
* @author James Ahlborn | |||||
*/ | |||||
public class AutoNumberTest extends TestCase | |||||
{ | |||||
public AutoNumberTest(String name) throws Exception { | |||||
super(name); | |||||
} | |||||
public void testAutoNumber() throws Exception | |||||
{ | |||||
for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) { | |||||
Database db = createMem(fileFormat); | |||||
Table table = new TableBuilder("test") | |||||
.addColumn(new ColumnBuilder("a", DataType.LONG) | |||||
.setAutoNumber(true)) | |||||
.addColumn(new ColumnBuilder("b", DataType.TEXT)) | |||||
.toTable(db); | |||||
doTestAutoNumber(table); | |||||
db.close(); | |||||
} | |||||
} | |||||
public void testAutoNumberPK() throws Exception | |||||
{ | |||||
for (final TestDB testDB : SUPPORTED_DBS_TEST) { | |||||
Database db = openMem(testDB); | |||||
Table table = db.getTable("Table3"); | |||||
doTestAutoNumber(table); | |||||
db.close(); | |||||
} | |||||
} | |||||
private static void doTestAutoNumber(Table table) throws Exception | |||||
{ | |||||
Object[] row = {null, "row1"}; | |||||
assertSame(row, table.addRow(row)); | |||||
assertEquals(1, ((Integer)row[0]).intValue()); | |||||
row = table.addRow(13, "row2"); | |||||
assertEquals(2, ((Integer)row[0]).intValue()); | |||||
row = table.addRow("flubber", "row3"); | |||||
assertEquals(3, ((Integer)row[0]).intValue()); | |||||
table.reset(); | |||||
row = table.addRow(Column.AUTO_NUMBER, "row4"); | |||||
assertEquals(4, ((Integer)row[0]).intValue()); | |||||
row = table.addRow(Column.AUTO_NUMBER, "row5"); | |||||
assertEquals(5, ((Integer)row[0]).intValue()); | |||||
Object[] smallRow = {Column.AUTO_NUMBER}; | |||||
row = table.addRow(smallRow); | |||||
assertNotSame(row, smallRow); | |||||
assertEquals(6, ((Integer)row[0]).intValue()); | |||||
table.reset(); | |||||
List<? extends Map<String, Object>> expectedRows = | |||||
createExpectedTable( | |||||
createExpectedRow( | |||||
"a", 1, | |||||
"b", "row1"), | |||||
createExpectedRow( | |||||
"a", 2, | |||||
"b", "row2"), | |||||
createExpectedRow( | |||||
"a", 3, | |||||
"b", "row3"), | |||||
createExpectedRow( | |||||
"a", 4, | |||||
"b", "row4"), | |||||
createExpectedRow( | |||||
"a", 5, | |||||
"b", "row5"), | |||||
createExpectedRow( | |||||
"a", 6, | |||||
"b", null)); | |||||
assertTable(expectedRows, table); | |||||
} | |||||
public void testAutoNumberGuid() throws Exception | |||||
{ | |||||
for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) { | |||||
Database db = createMem(fileFormat); | |||||
Table table = new TableBuilder("test") | |||||
.addColumn(new ColumnBuilder("a", DataType.GUID) | |||||
.setAutoNumber(true)) | |||||
.addColumn(new ColumnBuilder("b", DataType.TEXT)) | |||||
.toTable(db); | |||||
Object[] row = {null, "row1"}; | |||||
assertSame(row, table.addRow(row)); | |||||
assertTrue(ColumnImpl.isGUIDValue(row[0])); | |||||
row = table.addRow(13, "row2"); | |||||
assertTrue(ColumnImpl.isGUIDValue(row[0])); | |||||
row = table.addRow("flubber", "row3"); | |||||
assertTrue(ColumnImpl.isGUIDValue(row[0])); | |||||
Object[] smallRow = {Column.AUTO_NUMBER}; | |||||
row = table.addRow(smallRow); | |||||
assertNotSame(row, smallRow); | |||||
assertTrue(ColumnImpl.isGUIDValue(row[0])); | |||||
db.close(); | |||||
} | |||||
} | |||||
public void testInsertLongAutoNumber() throws Exception | |||||
{ | |||||
for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) { | |||||
Database db = createMem(fileFormat); | |||||
Table table = new TableBuilder("test") | |||||
.addColumn(new ColumnBuilder("a", DataType.LONG) | |||||
.setAutoNumber(true)) | |||||
.addColumn(new ColumnBuilder("b", DataType.TEXT)) | |||||
.toTable(db); | |||||
doTestInsertLongAutoNumber(table); | |||||
db.close(); | |||||
} | |||||
} | |||||
public void testInsertLongAutoNumberPK() throws Exception | |||||
{ | |||||
for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) { | |||||
Database db = createMem(fileFormat); | |||||
Table table = new TableBuilder("test") | |||||
.addColumn(new ColumnBuilder("a", DataType.LONG) | |||||
.setAutoNumber(true)) | |||||
.addColumn(new ColumnBuilder("b", DataType.TEXT)) | |||||
.setPrimaryKey("a") | |||||
.toTable(db); | |||||
doTestInsertLongAutoNumber(table); | |||||
db.close(); | |||||
} | |||||
} | |||||
private static void doTestInsertLongAutoNumber(Table table) throws Exception | |||||
{ | |||||
assertFalse(table.getDatabase().isAllowAutoNumberInsert()); | |||||
assertFalse(table.isAllowAutoNumberInsert()); | |||||
Object[] row = {null, "row1"}; | |||||
assertSame(row, table.addRow(row)); | |||||
assertEquals(1, ((Integer)row[0]).intValue()); | |||||
row = table.addRow(13, "row2"); | |||||
assertEquals(2, ((Integer)row[0]).intValue()); | |||||
row = table.addRow("flubber", "row3"); | |||||
assertEquals(3, ((Integer)row[0]).intValue()); | |||||
table.reset(); | |||||
table.setAllowAutoNumberInsert(true); | |||||
assertFalse(table.getDatabase().isAllowAutoNumberInsert()); | |||||
assertTrue(table.isAllowAutoNumberInsert()); | |||||
Row row2 = CursorBuilder.findRow( | |||||
table, Collections.singletonMap("a", 2)); | |||||
assertEquals("row2", row2.getString("b")); | |||||
table.deleteRow(row2); | |||||
row = table.addRow(Column.AUTO_NUMBER, "row4"); | |||||
assertEquals(4, ((Integer)row[0]).intValue()); | |||||
assertEquals(4, ((TableImpl)table).getLastLongAutoNumber()); | |||||
row = table.addRow(2, "row2-redux"); | |||||
assertEquals(2, ((Integer)row[0]).intValue()); | |||||
assertEquals(4, ((TableImpl)table).getLastLongAutoNumber()); | |||||
row2 = CursorBuilder.findRow( | |||||
table, Collections.singletonMap("a", 2)); | |||||
assertEquals("row2-redux", row2.getString("b")); | |||||
row = table.addRow(13, "row13-mindthegap"); | |||||
assertEquals(13, ((Integer)row[0]).intValue()); | |||||
assertEquals(13, ((TableImpl)table).getLastLongAutoNumber()); | |||||
try { | |||||
table.addRow("not a number", "nope"); | |||||
fail("NumberFormatException should have been thrown"); | |||||
} catch(NumberFormatException e) { | |||||
// success | |||||
} | |||||
assertEquals(13, ((TableImpl)table).getLastLongAutoNumber()); | |||||
try { | |||||
table.addRow(-10, "uh-uh"); | |||||
fail("IOException should have been thrown"); | |||||
} catch(IOException e) { | |||||
// success | |||||
} | |||||
row = table.addRow(Column.AUTO_NUMBER, "row14"); | |||||
assertEquals(14, ((Integer)row[0]).intValue()); | |||||
Row row13 = CursorBuilder.findRow( | |||||
table, Collections.singletonMap("a", 13)); | |||||
assertEquals("row13-mindthegap", row13.getString("b")); | |||||
row13.put("a", "45"); | |||||
row13 = table.updateRow(row13); | |||||
assertEquals(45, row13.get("a")); | |||||
assertEquals(45, ((TableImpl)table).getLastLongAutoNumber()); | |||||
row13.put("a", -1); | |||||
try { | |||||
table.updateRow(row13); | |||||
fail("IOException should have been thrown"); | |||||
} catch(IOException e) { | |||||
// success | |||||
} | |||||
assertEquals(45, ((TableImpl)table).getLastLongAutoNumber()); | |||||
row13.put("a", 55); | |||||
table.setAllowAutoNumberInsert(null); | |||||
row13 = table.updateRow(row13); | |||||
assertEquals(45, row13.get("a")); | |||||
assertEquals(45, ((TableImpl)table).getLastLongAutoNumber()); | |||||
} | |||||
public void testInsertComplexAutoNumber() throws Exception | |||||
{ | |||||
for(final TestDB testDB : TestDB.getSupportedForBasename(Basename.COMPLEX)) { | |||||
Database db = openMem(testDB); | |||||
Table t1 = db.getTable("Table1"); | |||||
assertFalse(t1.isAllowAutoNumberInsert()); | |||||
int lastAutoNum = ((TableImpl)t1).getLastComplexTypeAutoNumber(); | |||||
Object[] row = t1.addRow("arow"); | |||||
++lastAutoNum; | |||||
checkAllComplexAutoNums(lastAutoNum, row); | |||||
assertEquals(lastAutoNum, ((TableImpl)t1).getLastComplexTypeAutoNumber()); | |||||
db.setAllowAutoNumberInsert(true); | |||||
assertTrue(db.isAllowAutoNumberInsert()); | |||||
assertTrue(t1.isAllowAutoNumberInsert()); | |||||
row = t1.addRow("anotherrow"); | |||||
++lastAutoNum; | |||||
checkAllComplexAutoNums(lastAutoNum, row); | |||||
assertEquals(lastAutoNum, ((TableImpl)t1).getLastComplexTypeAutoNumber()); | |||||
row = t1.addRow("row5", 5, null, null, 5, 5); | |||||
checkAllComplexAutoNums(5, row); | |||||
assertEquals(lastAutoNum, ((TableImpl)t1).getLastComplexTypeAutoNumber()); | |||||
row = t1.addRow("row13", 13, null, null, 13, 13); | |||||
checkAllComplexAutoNums(13, row); | |||||
assertEquals(13, ((TableImpl)t1).getLastComplexTypeAutoNumber()); | |||||
try { | |||||
t1.addRow("nope", "not a number"); | |||||
fail("NumberFormatException should have been thrown"); | |||||
} catch(NumberFormatException e) { | |||||
// success | |||||
} | |||||
assertEquals(13, ((TableImpl)t1).getLastComplexTypeAutoNumber()); | |||||
try { | |||||
t1.addRow("uh-uh", -10); | |||||
fail("IOException should have been thrown"); | |||||
} catch(IOException e) { | |||||
// success | |||||
} | |||||
assertEquals(13, ((TableImpl)t1).getLastComplexTypeAutoNumber()); | |||||
try { | |||||
t1.addRow("wut", 6, null, null, 40, 42); | |||||
fail("IOException should have been thrown"); | |||||
} catch(IOException e) { | |||||
// success | |||||
} | |||||
row = t1.addRow("morerows"); | |||||
checkAllComplexAutoNums(14, row); | |||||
assertEquals(14, ((TableImpl)t1).getLastComplexTypeAutoNumber()); | |||||
Row row13 = CursorBuilder.findRow( | |||||
t1, Collections.singletonMap("id", "row13")); | |||||
row13.put("VersionHistory_F5F8918F-0A3F-4DA9-AE71-184EE5012880", "45"); | |||||
row13.put("multi-value-data", "45"); | |||||
row13.put("attach-data", "45"); | |||||
row13 = t1.updateRow(row13); | |||||
checkAllComplexAutoNums(45, row13); | |||||
assertEquals(45, ((TableImpl)t1).getLastComplexTypeAutoNumber()); | |||||
row13.put("attach-data", -1); | |||||
try { | |||||
t1.updateRow(row13); | |||||
fail("IOException should have been thrown"); | |||||
} catch(IOException e) { | |||||
// success | |||||
} | |||||
assertEquals(45, ((TableImpl)t1).getLastComplexTypeAutoNumber()); | |||||
row13.put("attach-data", 55); | |||||
try { | |||||
t1.updateRow(row13); | |||||
fail("IOException should have been thrown"); | |||||
} catch(IOException e) { | |||||
// success | |||||
} | |||||
assertEquals(45, ((TableImpl)t1).getLastComplexTypeAutoNumber()); | |||||
row13.put("VersionHistory_F5F8918F-0A3F-4DA9-AE71-184EE5012880", 55); | |||||
row13.put("multi-value-data", 55); | |||||
db.setAllowAutoNumberInsert(null); | |||||
row13 = t1.updateRow(row13); | |||||
checkAllComplexAutoNums(45, row13); | |||||
assertEquals(45, ((TableImpl)t1).getLastComplexTypeAutoNumber()); | |||||
db.close(); | |||||
} | |||||
} | |||||
private static void checkAllComplexAutoNums(int expected, Object[] row) | |||||
{ | |||||
assertEquals(expected, ((ComplexValueForeignKey)row[1]).get()); | |||||
assertEquals(expected, ((ComplexValueForeignKey)row[4]).get()); | |||||
assertEquals(expected, ((ComplexValueForeignKey)row[5]).get()); | |||||
} | |||||
private static void checkAllComplexAutoNums(int expected, Row row) | |||||
{ | |||||
assertEquals(expected, ((Number)row.get("VersionHistory_F5F8918F-0A3F-4DA9-AE71-184EE5012880")).intValue()); | |||||
assertEquals(expected, ((Number)row.get("multi-value-data")).intValue()); | |||||
assertEquals(expected, ((Number)row.get("attach-data")).intValue()); | |||||
} | |||||
public void testInsertGuidAutoNumber() throws Exception | |||||
{ | |||||
for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) { | |||||
Database db = createMem(fileFormat); | |||||
Table table = new TableBuilder("test") | |||||
.addColumn(new ColumnBuilder("a", DataType.GUID) | |||||
.setAutoNumber(true)) | |||||
.addColumn(new ColumnBuilder("b", DataType.TEXT)) | |||||
.toTable(db); | |||||
db.setAllowAutoNumberInsert(true); | |||||
table.setAllowAutoNumberInsert(false); | |||||
assertFalse(table.isAllowAutoNumberInsert()); | |||||
Object[] row = {null, "row1"}; | |||||
assertSame(row, table.addRow(row)); | |||||
assertTrue(ColumnImpl.isGUIDValue(row[0])); | |||||
row = table.addRow(13, "row2"); | |||||
assertTrue(ColumnImpl.isGUIDValue(row[0])); | |||||
row = table.addRow("flubber", "row3"); | |||||
assertTrue(ColumnImpl.isGUIDValue(row[0])); | |||||
Object[] smallRow = {Column.AUTO_NUMBER}; | |||||
row = table.addRow(smallRow); | |||||
assertNotSame(row, smallRow); | |||||
assertTrue(ColumnImpl.isGUIDValue(row[0])); | |||||
table.setAllowAutoNumberInsert(null); | |||||
assertTrue(table.isAllowAutoNumberInsert()); | |||||
Row row2 = CursorBuilder.findRow( | |||||
table, Collections.singletonMap("b", "row2")); | |||||
assertEquals("row2", row2.getString("b")); | |||||
String row2Guid = row2.getString("a"); | |||||
table.deleteRow(row2); | |||||
row = table.addRow(Column.AUTO_NUMBER, "row4"); | |||||
assertTrue(ColumnImpl.isGUIDValue(row[0])); | |||||
row = table.addRow(row2Guid, "row2-redux"); | |||||
assertEquals(row2Guid, row[0]); | |||||
row2 = CursorBuilder.findRow( | |||||
table, Collections.singletonMap("a", row2Guid)); | |||||
assertEquals("row2-redux", row2.getString("b")); | |||||
try { | |||||
table.addRow("not a guid", "nope"); | |||||
fail("IOException should have been thrown"); | |||||
} catch(IOException e) { | |||||
// success | |||||
} | |||||
row = table.addRow(Column.AUTO_NUMBER, "row5"); | |||||
assertTrue(ColumnImpl.isGUIDValue(row[0])); | |||||
row2Guid = UUID.randomUUID().toString(); | |||||
row2.put("a", row2Guid); | |||||
row2 = table.updateRow(row2); | |||||
assertEquals(row2Guid, row2.get("a")); | |||||
row2.put("a", "not a guid"); | |||||
try { | |||||
table.updateRow(row2); | |||||
fail("IOException should have been thrown"); | |||||
} catch(IOException e) { | |||||
// success | |||||
} | |||||
table.setAllowAutoNumberInsert(false); | |||||
row2 = table.updateRow(row2); | |||||
assertTrue(ColumnImpl.isGUIDValue(row2.get("a"))); | |||||
assertFalse(row2Guid.equals(row2.get("a"))); | |||||
db.close(); | |||||
} | |||||
} | |||||
} |