From 8d88e1737d30f0017cb8fe4de6856482d38067d8 Mon Sep 17 00:00:00 2001 From: James Ahlborn Date: Sat, 27 Aug 2016 04:02:40 +0000 Subject: [PATCH] implement fkref writing; more error context; make relationship name unique git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/mutateops@1012 f203690c-595d-4dc9-a70b-905162fa7fd2 --- .../jackcess/IndexBuilder.java | 8 +++++ .../jackcess/impl/DatabaseImpl.java | 28 +++++++++++++-- .../jackcess/impl/IndexImpl.java | 34 ++++++++++++++---- .../jackcess/impl/RelationshipCreator.java | 16 +++++---- .../jackcess/impl/TableCreator.java | 3 +- .../jackcess/impl/TableMutator.java | 35 ++++++++++++------- .../jackcess/impl/TableUpdater.java | 13 ++++++- 7 files changed, 108 insertions(+), 29 deletions(-) diff --git a/src/main/java/com/healthmarketscience/jackcess/IndexBuilder.java b/src/main/java/com/healthmarketscience/jackcess/IndexBuilder.java index 7822b2d..472740e 100644 --- a/src/main/java/com/healthmarketscience/jackcess/IndexBuilder.java +++ b/src/main/java/com/healthmarketscience/jackcess/IndexBuilder.java @@ -123,6 +123,14 @@ public class IndexBuilder return setUnique(); } + /** + * @usage _advanced_method_ + */ + public IndexBuilder setType(byte type) { + _type = type; + return this; + } + /** * Sets this index to enforce uniqueness. */ diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java index fb1120c..5bfac3c 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java @@ -1170,8 +1170,9 @@ public class DatabaseImpl implements Database { initRelationships(); - String name = creator.getPrimaryTable().getName() + - creator.getSecondaryTable().getName(); // FIXME make unique + String name = findUniqueRelationshipName( + creator.getPrimaryTable().getName() + + creator.getSecondaryTable().getName()); RelationshipImpl newRel = creator.createRelationshipImpl(name); ColumnImpl ccol = _relationships.getColumn(REL_COL_COLUMN_COUNT); @@ -1222,6 +1223,29 @@ public class DatabaseImpl implements Database _relationships = getRequiredSystemTable(TABLE_SYSTEM_RELATIONSHIPS); } } + + private String findUniqueRelationshipName(String origName) throws IOException { + Set names = new HashSet(); + + for(Row row : + CursorImpl.createCursor(_systemCatalog).newIterable().setColumnNames( + SYSTEM_CATALOG_COLUMNS)) + { + String name = row.getString(CAT_COL_NAME); + if (name != null && TYPE_RELATIONSHIP.equals(row.get(CAT_COL_TYPE))) { + names.add(toLookupName(name)); + } + } + + String baseName = toLookupName(origName); + String name = baseName; + int i = 0; + while(names.contains(name)) { + name = baseName + (++i); + } + + return ((i == 0) ? origName : (origName + i)); + } public List getQueries() throws IOException { diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java index 833ee98..8ea7ed8 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java @@ -360,12 +360,34 @@ public class IndexImpl implements Index, Comparable buffer.putInt(TableImpl.MAGIC_TABLE_NUMBER); // seemingly constant magic value which matches the table def buffer.putInt(idx.getIndexNumber()); // index num buffer.putInt(idxDataState.getIndexDataNumber()); // index data num - buffer.put((byte)0); // related table type - buffer.putInt(INVALID_INDEX_NUMBER); // related index num - buffer.putInt(0); // related table definition page number - buffer.put((byte)0); // cascade updates flag - buffer.put((byte)0); // cascade deletes flag - buffer.put(idx.getType()); // index type flags + + byte idxType = idx.getType(); + if(idxType != FOREIGN_KEY_INDEX_TYPE) { + buffer.put((byte)0); // related table type + buffer.putInt(INVALID_INDEX_NUMBER); // related index num + buffer.putInt(0); // related table definition page number + buffer.put((byte)0); // cascade updates flag + buffer.put((byte)0); // cascade deletes flag + } else { + ForeignKeyReference reference = mutator.getForeignKey(idx); + buffer.put(reference.getTableType()); // related table type + buffer.putInt(reference.getOtherIndexNumber()); // related index num + buffer.putInt(reference.getOtherTablePageNumber()); // related table definition page number + byte updateFlags = 0; + if(reference.isCascadeUpdates()) { + updateFlags |= CASCADE_UPDATES_FLAG; + } + byte deleteFlags = 0; + if(reference.isCascadeDeletes()) { + deleteFlags |= CASCADE_DELETES_FLAG; + } + if(reference.isCascadeNullOnDelete()) { + deleteFlags |= CASCADE_NULL_FLAG; + } + buffer.put(updateFlags); // cascade updates flag + buffer.put(deleteFlags); // cascade deletes flag + } + buffer.put(idxType); // index type flags buffer.putInt(0); // unknown } diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipCreator.java b/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipCreator.java index 0d6fe26..c137f7f 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipCreator.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipCreator.java @@ -50,8 +50,7 @@ public class RelationshipCreator extends DBMutator // - secondary index name "" // - add 1, 2 after names to make unique (index names and // relationship names) - // FIXME - // - relationships also have entry in MSysObjects table + // - enforcing rel integrity can't have dupe cols public RelationshipCreator(DatabaseImpl database) { @@ -148,6 +147,9 @@ public class RelationshipCreator extends DBMutator return; } + // for now, we will require the unique index on the primary table (just + // like access does). we could just create it auto-magically... + // FIXME // - same number cols @@ -227,8 +229,8 @@ public class RelationshipCreator extends DBMutator return cols; } - private static Collection getColumnNames( - List cols, Collection colNames) { + private static List getColumnNames(List cols) { + List colNames = new ArrayList(); for(ColumnImpl col : cols) { colNames.add(col.getName()); } @@ -239,12 +241,12 @@ public class RelationshipCreator extends DBMutator // a relationship is one to one if the two sides of the relationship have // unique indexes on the relevant columns IndexImpl idx = _primaryTable.findIndexForColumns( - getColumnNames(_primaryCols, new HashSet()), true); + getColumnNames(_primaryCols), true); if(idx == null) { return false; } idx = _secondaryTable.findIndexForColumns( - getColumnNames(_secondaryCols, new HashSet()), true); + getColumnNames(_secondaryCols), true); return (idx != null); } @@ -255,7 +257,7 @@ public class RelationshipCreator extends DBMutator tableName = table.getName(); } if(cols != null) { - colNames = getColumnNames(cols, new ArrayList()); + colNames = getColumnNames(cols); } return CustomToStringStyle.valueBuilder(tableName) diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java index 36c866d..df6076a 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableCreator.java @@ -333,7 +333,8 @@ public class TableCreator extends TableMutator (col1.getFlags() == col2.getFlags())); } - private String withErrorContext(String msg) { + @Override + protected String withErrorContext(String msg) { return msg + "(Table=" + getName() + ")"; } } diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableMutator.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableMutator.java index 494bfb7..5bc7e11 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/TableMutator.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableMutator.java @@ -50,18 +50,22 @@ public abstract class TableMutator extends DBMutator return _colOffsets; } + public IndexImpl.ForeignKeyReference getForeignKey(IndexBuilder idx) { + return null; + } + protected void validateColumn(Set colNames, ColumnBuilder column) { // FIXME for now, we can't create complex columns if(column.getType() == DataType.COMPLEX_TYPE) { - throw new UnsupportedOperationException( - "Complex column creation is not yet implemented"); + throw new UnsupportedOperationException(withErrorContext( + "Complex column creation is not yet implemented")); } column.validate(getFormat()); if(!colNames.add(column.getName().toUpperCase())) { - throw new IllegalArgumentException("duplicate column name: " + - column.getName()); + throw new IllegalArgumentException(withErrorContext( + "duplicate column name: " + column.getName())); } setColumnSortOrder(column); @@ -72,26 +76,31 @@ public abstract class TableMutator extends DBMutator index.validate(colNames, getFormat()); if(!idxNames.add(index.getName().toUpperCase())) { - throw new IllegalArgumentException("duplicate index name: " + - index.getName()); + throw new IllegalArgumentException(withErrorContext( + "duplicate index name: " + index.getName())); } if(index.isPrimaryKey()) { if(foundPk[0]) { - throw new IllegalArgumentException( - "found second primary key index: " + index.getName()); + throw new IllegalArgumentException(withErrorContext( + "found second primary key index: " + index.getName())); } foundPk[0] = true; + } else if(index.getType() == IndexImpl.FOREIGN_KEY_INDEX_TYPE) { + if(getForeignKey(index) == null) { + throw new IllegalArgumentException(withErrorContext( + "missing foreign key info for " + index.getName())); + } } } - protected static void validateAutoNumberColumn(Set autoTypes, - ColumnBuilder column) + protected void validateAutoNumberColumn(Set autoTypes, + ColumnBuilder column) { if(!column.getType().isMultipleAutoNumberAllowed() && !autoTypes.add(column.getType())) { - throw new IllegalArgumentException( + throw new IllegalArgumentException(withErrorContext( "Can have at most one AutoNumber column of type " + column.getType() + - " per table"); + " per table")); } } @@ -112,6 +121,8 @@ public abstract class TableMutator extends DBMutator public abstract IndexDataState getIndexDataState(IndexBuilder idx); + protected abstract String withErrorContext(String msg); + /** * Maintains additional state used during column writing. * @usage _advanced_class_ diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java index 54bfd68..a6cbdea 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java @@ -45,6 +45,7 @@ public class TableUpdater extends TableMutator private List _nextPages = new ArrayList(1); private ColumnState _colState; private IndexDataState _idxDataState; + private IndexImpl.ForeignKeyReference _fkReference; public TableUpdater(TableImpl table) { super(table.getDatabase()); @@ -89,6 +90,15 @@ public class TableUpdater extends TableMutator return ((idx == _index) ? _idxDataState : null); } + void setForeignKey(IndexImpl.ForeignKeyReference fkReference) { + _fkReference = fkReference; + } + + @Override + public IndexImpl.ForeignKeyReference getForeignKey(IndexBuilder idx) { + return ((idx == _index) ? _fkReference : null); + } + int getAddedTdefLen() { return _addedTdefLen; } @@ -294,7 +304,8 @@ public class TableUpdater extends TableMutator (col2.getFlags() | ignoreColFlags))); } - private String withErrorContext(String msg) { + @Override + protected String withErrorContext(String msg) { String objStr = ""; if(_column != null) { objStr = ";Column=" + _column.getName(); -- 2.39.5