/** * Checks if the given <code>InsertRow</code>s contain cyclic fk constraints. * * @param rows * @return steps of the cycle, or null if there is none */ private static Collection<InsertRow> findCycle(Collection<InsertRow> rows) { Iterator<InsertRow> rowsIter = rows.iterator(); while (rowsIter.hasNext()) { InsertRow begin = rowsIter.next(); Iterator<InsertRow> refIter = begin.getReferencedRows().iterator(); while (refIter.hasNext()) { InsertRow referencedRow = refIter.next(); List<InsertRow> cycle = findCycleRecursion(referencedRow, begin, new ArrayList<InsertRow>()); if (cycle != null) { return cycle; } } } return null; }
private static List<InsertRow> findCycleRecursion( InsertRow next, InsertRow begin, List<InsertRow> steps) { if (steps.contains(next)) { steps.add(next); return steps; } steps.add(next); Iterator<InsertRow> refIter = next.getReferencedRows().iterator(); while (refIter.hasNext()) { InsertRow referencedRow = refIter.next(); List<InsertRow> cycle = findCycleRecursion(referencedRow, begin, steps); if (cycle != null) { return cycle; } } steps.remove(steps.size() - 1); return null; }
/** * Sorts the given <code>InsertRow</code>s topologically (respecting the foreign key constraints), * so they can be inserted in the resulting order without causing foreign key violations. * * <p>Number of precedessors (pre): number of fields that *are referenced by* this row Number of * successors (post) : number of fields that *reference* this row * * @param inserts insert rows to sort * @return the nodes of the graph in topological order if no cycle is present, else in arbitrary * order */ public static List<InsertRow> getInsertOrder(List<InsertRow> inserts) { List<InsertRow> result = new ArrayList<InsertRow>(); // key: inserts, value: number of fields that are referenced by this row Map<InsertRow, Integer> preMap = new HashMap<InsertRow, Integer>(); // key: inserts with no foreign key constraints List<InsertRow> noPre = new ArrayList<InsertRow>(); // build map Iterator<InsertRow> insertIter = inserts.iterator(); while (insertIter.hasNext()) { InsertRow insertRow = insertIter.next(); int pre = insertRow.getReferencedFields().size(); LOG.logDebug("Adding row to preMap: " + insertRow); preMap.put(insertRow, pre); if (pre == 0) { noPre.add(insertRow); } } while (!noPre.isEmpty()) { // select an insert row that has no open fk constraints InsertRow insertRow = noPre.get(0); noPre.remove(0); result.add(insertRow); // decrease the number of pending fk constraints for all insert rows that // reference the currently processed insert row Collection<InsertField> postList = insertRow.getReferencingFields(); Iterator<InsertField> iter = postList.iterator(); while (iter.hasNext()) { InsertField postField = iter.next(); if (preMap.get(postField.getRow()) == null) { LOG.logDebug("No pre info for: " + postField.getRow()); } int pre = preMap.get(postField.getRow()); preMap.put(postField.getRow(), --pre); if (pre == 0) { noPre.add(postField.getRow()); } } } if (result.size() != inserts.size()) { Collection<InsertRow> cycle = InsertRow.findCycle(inserts); Iterator<InsertRow> cycleIter = cycle.iterator(); StringBuffer sb = new StringBuffer(); while (cycleIter.hasNext()) { sb.append(cycleIter.next()); if (cycle.iterator().hasNext()) { sb.append(" -> "); } } String msg = Messages.getMessage("DATASTORE_FK_CYCLE", sb.toString()); LOG.logWarning(msg); result.clear(); result.addAll(inserts); } return result; }