diff options
Diffstat (limited to 'src')
5 files changed, 173 insertions, 41 deletions
diff --git a/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java b/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java index 9be67d2..abebec0 100644 --- a/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java +++ b/src/main/java/com/healthmarketscience/jackcess/ColumnBuilder.java @@ -384,12 +384,12 @@ public class ColumnBuilder { * @usage _advanced_method_ */ public void validate(JetFormat format) { - if(getType() == null) { - throw new IllegalArgumentException(withErrorContext("must have type")); - } DatabaseImpl.validateIdentifierName( getName(), format.MAX_COLUMN_NAME_LENGTH, "column"); + if(getType() == null) { + throw new IllegalArgumentException(withErrorContext("must have type")); + } if(getType().isUnsupported()) { throw new IllegalArgumentException(withErrorContext( "Cannot create column with unsupported type " + getType())); diff --git a/src/main/java/com/healthmarketscience/jackcess/RelationshipBuilder.java b/src/main/java/com/healthmarketscience/jackcess/RelationshipBuilder.java index 8f7ae44..795bbd9 100644 --- a/src/main/java/com/healthmarketscience/jackcess/RelationshipBuilder.java +++ b/src/main/java/com/healthmarketscience/jackcess/RelationshipBuilder.java @@ -17,45 +17,91 @@ limitations under the License. package com.healthmarketscience.jackcess; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import com.healthmarketscience.jackcess.impl.DatabaseImpl; +import com.healthmarketscience.jackcess.impl.RelationshipCreator; import com.healthmarketscience.jackcess.impl.RelationshipImpl; /** + * Builder style class for constructing a {@link Relationship}, and, + * optionally, the associated backing foreign key (if referential integrity + * enforcement is enabled). * * @author James Ahlborn */ public class RelationshipBuilder { + private static final int JOIN_FLAGS = + RelationshipImpl.LEFT_OUTER_JOIN_FLAG | + RelationshipImpl.RIGHT_OUTER_JOIN_FLAG; + /** relationship flags (default to "don't enforce") */ private int _flags = RelationshipImpl.NO_REFERENTIAL_INTEGRITY_FLAG; + private String _toTable; + private String _fromTable; + private List<String> _toCols = new ArrayList<String>(); + private List<String> _fromCols = new ArrayList<String>(); - // - primary table must have unique index - // - primary table index name ".rC", ".rD"... - // - secondary index name "<PTable><STable>" - // - add <name>1, <name>2 after names to make unique (index names and - // relationship names) - - public RelationshipBuilder() + public RelationshipBuilder(String toTable, String fromTable) { + _toTable = toTable; + _fromTable = fromTable; + } + + /** + * Adds a pair of columns to the relationship. + */ + public RelationshipBuilder addColumns(String toCol, String fromCol) { + _toCols.add(toCol); + _fromCols.add(fromCol); + return this; } + /** + * Enables referential integrity enforcement for this relationship. + * + * Note, this requires the "to" table to have an existing unique index on + * the relevant columns. + */ public RelationshipBuilder setReferentialIntegrity() { return clearFlag(RelationshipImpl.NO_REFERENTIAL_INTEGRITY_FLAG); } + /** + * Enables deletes to be cascaded from the "to" table to the "from" table. + * + * Note, this requires referential integrity to be enforced. + */ public RelationshipBuilder setCascadeDeletes() { return setFlag(RelationshipImpl.CASCADE_DELETES_FLAG); } + /** + * Enables updates to be cascaded from the "to" table to the "from" table. + * + * Note, this requires referential integrity to be enforced. + */ public RelationshipBuilder setCascadeUpdates() { return setFlag(RelationshipImpl.CASCADE_UPDATES_FLAG); } + /** + * Enables deletes in the "to" table to be cascaded as "null" to the "from" + * table. + * + * Note, this requires referential integrity to be enforced. + */ public RelationshipBuilder setCascadeNullOnDelete() { return setFlag(RelationshipImpl.CASCADE_NULL_FLAG); } - + + /** + * Sets the preferred join type for this relationship. + */ public RelationshipBuilder setJoinType(Relationship.JoinType joinType) { + clearFlag(JOIN_FLAGS); switch(joinType) { case INNER: // nothing to do @@ -80,6 +126,22 @@ public class RelationshipBuilder return _flags; } + public String getToTable() { + return _toTable; + } + + public String getFromTable() { + return _fromTable; + } + + public List<String> getToColumns() { + return _toCols; + } + + public List<String> getFromColumns() { + return _fromCols; + } + /** * Creates a new Relationship in the given Database with the currently * configured attributes. @@ -87,10 +149,7 @@ public class RelationshipBuilder public Relationship toRelationship(Database db) throws IOException { - - - // FIXME writeme - return null; + return new RelationshipCreator((DatabaseImpl)db).createRelationship(this); } private RelationshipBuilder setFlag(int flagMask) { diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipCreator.java b/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipCreator.java index 273a8ae..452c176 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipCreator.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipCreator.java @@ -17,6 +17,7 @@ limitations under the License. package com.healthmarketscience.jackcess.impl; import java.io.IOException; +import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -29,12 +30,23 @@ import com.healthmarketscience.jackcess.RelationshipBuilder; */ public class RelationshipCreator extends DBMutator { + private final static int CASCADE_FLAGS = + RelationshipImpl.CASCADE_DELETES_FLAG | + RelationshipImpl.CASCADE_UPDATES_FLAG | + RelationshipImpl.CASCADE_NULL_FLAG; + + private TableImpl _primaryTable; private TableImpl _secondaryTable; private RelationshipBuilder _relationship; private List<ColumnImpl> _primaryCols; private List<ColumnImpl> _secondaryCols; - private int _flags; + + // - primary table must have unique index + // - primary table index name ".rC", ".rD"... + // - secondary index name "<PTable><STable>" + // - add <name>1, <name>2 after names to make unique (index names and + // relationship names) public RelationshipCreator(DatabaseImpl database) { @@ -66,7 +78,7 @@ public class RelationshipCreator extends DBMutator throws IOException { _relationship = relationship; - + validate(); getPageChannel().startExclusiveWrite(); @@ -85,22 +97,26 @@ public class RelationshipCreator extends DBMutator private void validate() throws IOException { + _primaryTable = getDatabase().getTable(_relationship.getToTable()); + _secondaryTable = getDatabase().getTable(_relationship.getToTable()); + if((_primaryTable == null) || (_secondaryTable == null)) { - throw new IllegalArgumentException( - "Two tables are required in relationship"); - } - if(_primaryTable.getDatabase() != _secondaryTable.getDatabase()) { - throw new IllegalArgumentException("Tables are not from same database"); + throw new IllegalArgumentException(withErrorContext( + "Two valid tables are required in relationship")); } + _primaryCols = getColumns(_primaryTable, _relationship.getToColumns()); + _secondaryCols = getColumns(_secondaryTable, _relationship.getFromColumns()); + if((_primaryCols == null) || (_primaryCols.isEmpty()) || (_secondaryCols == null) || (_secondaryCols.isEmpty())) { - throw new IllegalArgumentException("Missing columns in relationship"); + throw new IllegalArgumentException(withErrorContext( + "Missing columns in relationship")); } if(_primaryCols.size() != _secondaryCols.size()) { - throw new IllegalArgumentException( - "Must have same number of columns on each side of relationship"); + throw new IllegalArgumentException(withErrorContext( + "Must have same number of columns on each side of relationship")); } for(int i = 0; i < _primaryCols.size(); ++i) { @@ -108,12 +124,18 @@ public class RelationshipCreator extends DBMutator ColumnImpl scol = _primaryCols.get(i); if(pcol.getType() != scol.getType()) { - throw new IllegalArgumentException( - "Matched columns must have the same data type"); + throw new IllegalArgumentException(withErrorContext( + "Matched columns must have the same data type")); } } if(!_relationship.hasReferentialIntegrity()) { + + if((_relationship.getFlags() & CASCADE_FLAGS) != 0) { + throw new IllegalArgumentException(withErrorContext( + "Cascade flags cannot be enabled if referential integrity is not enforced")); + } + return; } @@ -123,7 +145,8 @@ public class RelationshipCreator extends DBMutator // - cols come from right tables, tables from right db // - (cols can be duped in index) // - cols have same data types - // - if enforce, require unique index on primary (auto-create?), index on secondary + // - if enforce, require unique index on primary, + // - auto-create index on secondary // - advanced, check for enforce cycles? // - index must be ascending @@ -186,4 +209,38 @@ public class RelationshipCreator extends DBMutator } } } + + private static List<ColumnImpl> getColumns(TableImpl table, List<String> colNames) { + List<ColumnImpl> cols = new ArrayList<ColumnImpl>(); + for(String colName : colNames) { + cols.add(table.getColumn(colName)); + } + return cols; + } + + private static String getTableErrorContext( + TableImpl table, List<ColumnImpl> cols, + String tableName, List<String> colNames) { + if(table != null) { + tableName = table.getName(); + } + if(cols != null) { + colNames = new ArrayList<String>(); + for(ColumnImpl col : cols) { + colNames.add(col.getName()); + } + } + + return CustomToStringStyle.valueBuilder(tableName) + .append(null, cols) + .toString(); + } + + private String withErrorContext(String msg) { + return msg + "(Rel=" + + getTableErrorContext(_primaryTable, _primaryCols, _relationship.getToTable(), + _relationship.getToColumns()) + " <- " + + getTableErrorContext(_secondaryTable, _secondaryCols, _relationship.getFromTable(), + _relationship.getFromColumns()) + ")"; + } } diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java index 5fb3337..36c866d 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java @@ -104,7 +104,8 @@ public class TableCreator extends TableMutator } } } - throw new IllegalStateException("could not find state for index"); + throw new IllegalStateException(withErrorContext( + "could not find state for index")); } public List<IndexDataState> getIndexDataStates() { @@ -251,13 +252,13 @@ public class TableCreator extends TableMutator getDatabase().validateNewTableName(_name); if((_columns == null) || _columns.isEmpty()) { - throw new IllegalArgumentException( - "Cannot create table with no columns"); + throw new IllegalArgumentException(withErrorContext( + "Cannot create table with no columns")); } if(_columns.size() > getFormat().MAX_COLUMNS_PER_TABLE) { - throw new IllegalArgumentException( + throw new IllegalArgumentException(withErrorContext( "Cannot create table with more than " + - getFormat().MAX_COLUMNS_PER_TABLE + " columns"); + getFormat().MAX_COLUMNS_PER_TABLE + " columns")); } Set<String> colNames = new HashSet<String>(); @@ -278,9 +279,9 @@ public class TableCreator extends TableMutator if(hasIndexes()) { if(_indexes.size() > getFormat().MAX_INDEXES_PER_TABLE) { - throw new IllegalArgumentException( + throw new IllegalArgumentException(withErrorContext( "Cannot create table with more than " + - getFormat().MAX_INDEXES_PER_TABLE + " indexes"); + getFormat().MAX_INDEXES_PER_TABLE + " indexes")); } // now, validate the indexes @@ -332,4 +333,7 @@ public class TableCreator extends TableMutator (col1.getFlags() == col2.getFlags())); } + private String withErrorContext(String msg) { + return msg + "(Table=" + getName() + ")"; + } } diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java index ecaa10e..54bfd68 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java @@ -173,12 +173,13 @@ public class TableUpdater extends TableMutator private void validateAddColumn() { if(_column == null) { - throw new IllegalArgumentException("Cannot add column with no column"); + throw new IllegalArgumentException(withErrorContext( + "Cannot add column with no column")); } if((_table.getColumnCount() + 1) > getFormat().MAX_COLUMNS_PER_TABLE) { - throw new IllegalArgumentException( + throw new IllegalArgumentException(withErrorContext( "Cannot add column to table with " + - getFormat().MAX_COLUMNS_PER_TABLE + " columns"); + getFormat().MAX_COLUMNS_PER_TABLE + " columns")); } Set<String> colNames = getColumnNames(); @@ -199,12 +200,13 @@ public class TableUpdater extends TableMutator private void validateAddIndex() { if(_index == null) { - throw new IllegalArgumentException("Cannot add index with no index"); + throw new IllegalArgumentException(withErrorContext( + "Cannot add index with no index")); } if((_table.getLogicalIndexCount() + 1) > getFormat().MAX_INDEXES_PER_TABLE) { - throw new IllegalArgumentException( + throw new IllegalArgumentException(withErrorContext( "Cannot add index to table with " + - getFormat().MAX_INDEXES_PER_TABLE + " indexes"); + getFormat().MAX_INDEXES_PER_TABLE + " indexes")); } boolean foundPk[] = new boolean[1]; @@ -291,4 +293,14 @@ public class TableUpdater extends TableMutator ((col1.getFlags() | ignoreColFlags) == (col2.getFlags() | ignoreColFlags))); } + + private String withErrorContext(String msg) { + String objStr = ""; + if(_column != null) { + objStr = ";Column=" + _column.getName(); + } else if(_index != null) { + objStr = ";Index=" + _index.getName(); + } + return msg + "(Table=" + _table.getName() + objStr + ")"; + } } |