From 6d1576e9f100a3a96c6c3f1321e3c8bf72c46cb9 Mon Sep 17 00:00:00 2001 From: James Ahlborn Date: Tue, 30 Aug 2016 02:24:33 +0000 Subject: [PATCH] implement adding indexes for integ enforced relationships git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/mutateops@1014 f203690c-595d-4dc9-a70b-905162fa7fd2 --- .../jackcess/impl/FKEnforcer.java | 1 + .../jackcess/impl/IndexImpl.java | 2 +- .../jackcess/impl/RelationshipCreator.java | 70 +++++++++++++++++-- .../jackcess/impl/TableUpdater.java | 14 +++- 4 files changed, 78 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/FKEnforcer.java b/src/main/java/com/healthmarketscience/jackcess/impl/FKEnforcer.java index d29836a..400446b 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/FKEnforcer.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/FKEnforcer.java @@ -251,6 +251,7 @@ final class FKEnforcer // ensure that the relevant rows exist in the primary tables for which // this table is a secondary table. however, null values are allowed if(!areNull(joiner, row) && !joiner.hasRows(row)) { + // FIXME, add error context here? throw new ConstraintViolationException( "Adding new row " + Arrays.asList(row) + " violates constraint " + joiner.toFKString()); diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java index 8ea7ed8..0cf07cf 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/IndexImpl.java @@ -54,7 +54,7 @@ public class IndexImpl implements Index, Comparable private static final byte CASCADE_NULL_FLAG = (byte)2; /** index table type for the "primary" table in a foreign key index */ - private static final byte PRIMARY_TABLE_TYPE = (byte)1; + static final byte PRIMARY_TABLE_TYPE = (byte)1; /** indicate an invalid index number for foreign key field */ private static final int INVALID_INDEX_NUMBER = -1; diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipCreator.java b/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipCreator.java index 550d498..64e5abb 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipCreator.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/RelationshipCreator.java @@ -23,8 +23,11 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import com.healthmarketscience.jackcess.ConstraintViolationException; import com.healthmarketscience.jackcess.IndexBuilder; +import com.healthmarketscience.jackcess.IndexCursor; import com.healthmarketscience.jackcess.RelationshipBuilder; +import com.healthmarketscience.jackcess.Row; /** * @@ -94,9 +97,10 @@ public class RelationshipCreator extends DBMutator RelationshipImpl newRel = getDatabase().writeRelationship(this); - // FIXME, handle indexes - - // FIXME, enforce ref integ before adding indexes! + if(_relationship.hasReferentialIntegrity()) { + addPrimaryIndex(); + addSecondaryIndex(); + } return newRel; @@ -105,6 +109,39 @@ public class RelationshipCreator extends DBMutator } } + private void addPrimaryIndex() throws IOException { + TableUpdater updater = new TableUpdater(_primaryTable); + updater.setForeignKey(createFKReference(true)); + updater.addIndex(createPrimaryIndex(), true); + } + + private void addSecondaryIndex() throws IOException { + TableUpdater updater = new TableUpdater(_secondaryTable); + updater.setForeignKey(createFKReference(false)); + updater.addIndex(createSecondaryIndex(), true); + } + + private IndexImpl.ForeignKeyReference createFKReference(boolean isPrimary) { + byte tableType = 0; + int otherTableNum = 0; + int otherIdxNum = 0; + if(isPrimary) { + tableType = IndexImpl.PRIMARY_TABLE_TYPE; + otherTableNum = _secondaryTable.getTableDefPageNumber(); + otherIdxNum = _secondaryTable.getLogicalIndexCount(); + } else { + otherTableNum = _primaryTable.getTableDefPageNumber(); + otherIdxNum = _primaryTable.getLogicalIndexCount(); + } + boolean cascadeUpdates = ((_flags & RelationshipImpl.CASCADE_UPDATES_FLAG) != 0); + boolean cascadeDeletes = ((_flags & RelationshipImpl.CASCADE_DELETES_FLAG) != 0); + boolean cascadeNull = ((_flags & RelationshipImpl.CASCADE_NULL_FLAG) != 0); + + return new IndexImpl.ForeignKeyReference( + tableType, otherIdxNum, otherTableNum, cascadeUpdates, cascadeDeletes, + cascadeNull); + } + private void validate() throws IOException { _primaryTable = getDatabase().getTable(_relationship.getToTable()); @@ -167,10 +204,31 @@ public class RelationshipCreator extends DBMutator "Cannot have duplicate columns in an integrity enforced relationship")); } + // TODO: future, check for enforce cycles? + // check referential integrity - // FIXME + IndexCursor primaryCursor = primaryIdx.newCursor().toIndexCursor(); + Object[] entryValues = new Object[_secondaryCols.size()]; + for(Row row : _secondaryTable.newCursor().toCursor() + .newIterable().addColumns(_secondaryCols)) { + // grab the from table values + boolean hasValues = false; + for(int i = 0; i < _secondaryCols.size(); ++i) { + entryValues[i] = _secondaryCols.get(i).getRowValue(row); + hasValues = hasValues || (entryValues[i] != null); + } + + if(!hasValues) { + // we can ignore null entries + continue; + } + + if(!primaryCursor.findFirstRowByEntry(entryValues)) { + throw new ConstraintViolationException(withErrorContext( + "Integrity constraint violation found for relationship")); + } + } - // TODO: future, check for enforce cycles? } private IndexBuilder createPrimaryIndex() { @@ -185,7 +243,7 @@ public class RelationshipCreator extends DBMutator String name = getUniqueIndexName(_secondaryTable); // FIXME? - return createIndex(name, _primaryCols) + return createIndex(name, _secondaryCols) .setType(IndexImpl.FOREIGN_KEY_INDEX_TYPE); } diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java index a6cbdea..43896be 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableUpdater.java @@ -145,10 +145,16 @@ public class TableUpdater extends TableMutator } public IndexImpl addIndex(IndexBuilder index) throws IOException { + return addIndex(index, false); + } + + IndexImpl addIndex(IndexBuilder index, boolean isInternal) throws IOException { _index = index; - validateAddIndex(); + if(!isInternal) { + validateAddIndex(); + } // assign index number and do some assorted index bookkeeping int indexNumber = _table.getLogicalIndexCount(); @@ -157,7 +163,11 @@ public class TableUpdater extends TableMutator // initialize backing index state initIndexDataState(); - getPageChannel().startExclusiveWrite(); + if(!isInternal) { + getPageChannel().startExclusiveWrite(); + } else { + getPageChannel().startWrite(); + } try { if(_idxDataState.getIndexDataNumber() == _table.getIndexCount()) { -- 2.39.5