static void checkCascadeDelete()

in HSQL/src/org/hsqldb1/Table.java [2311:2464]


    static void checkCascadeDelete(Session session, Table table,
                                   HashMappedList tableUpdateLists, Row row,
                                   boolean delete,
                                   HashSet path) throws HsqlException {

        for (int i = 0, size = table.constraintList.length; i < size; i++) {
            Constraint c = table.constraintList[i];

            if (c.getType() != Constraint.MAIN || c.getRef() == null) {
                continue;
            }

            RowIterator refiterator = c.findFkRef(session, row.getData(),
                                                  delete);

            if (!refiterator.hasNext()) {
                continue;
            }

            try {
                if (c.core.deleteAction == Constraint.NO_ACTION) {
                    if (c.core.mainTable == c.core.refTable) {
                        Row refrow = refiterator.next();

                        // fredt - it's the same row
                        // this supports deleting a single row
                        // in future we can iterate over and check against
                        // the full delete row list to enable multi-row
                        // with self-referencing FK's deletes
                        if (row.equals(refrow)) {
                            continue;
                        }
                    }

                    throw Trace.error(Trace.INTEGRITY_CONSTRAINT_VIOLATION,
                                      Trace.Constraint_violation,
                                      new Object[] {
                        c.core.fkName.name, c.core.refTable.getName().name
                    });
                }

                Table reftable = c.getRef();

                // shortcut when deltable has no imported constraint
                boolean hasref =
                    reftable.getNextConstraintIndex(0, Constraint.MAIN) != -1;

                // if (reftable == this) we don't need to go further and can return ??
                if (delete == false && hasref == false) {
                    continue;
                }

                Index    refindex  = c.getRefIndex();
                int[]    m_columns = c.getMainColumns();
                int[]    r_columns = c.getRefColumns();
                Object[] mdata     = row.getData();
                boolean isUpdate = c.getDeleteAction() == Constraint.SET_NULL
                                   || c.getDeleteAction()
                                      == Constraint.SET_DEFAULT;

                // -- list for records to be inserted if this is
                // -- a 'ON DELETE SET [NULL|DEFAULT]' constraint
                HashMappedList rowSet = null;

                if (isUpdate) {
                    rowSet = (HashMappedList) tableUpdateLists.get(reftable);

                    if (rowSet == null) {
                        rowSet = new HashMappedList();

                        tableUpdateLists.add(reftable, rowSet);
                    }
                }

                // walk the index for all the nodes that reference delnode
                for (;;) {
                    Row refrow = refiterator.next();

                    if (refrow == null || refrow.isCascadeDeleted()
                            || refindex.compareRowNonUnique(
                                session, mdata, m_columns,
                                refrow.getData()) != 0) {
                        break;
                    }

                    // -- if the constraint is a 'SET [DEFAULT|NULL]' constraint we have to keep
                    // -- a new record to be inserted after deleting the current. We also have to
                    // -- switch over to the 'checkCascadeUpdate' method below this level
                    if (isUpdate) {
                        Object[] rnd = reftable.getEmptyRowData();

                        System.arraycopy(refrow.getData(), 0, rnd, 0,
                                         rnd.length);

                        if (c.getDeleteAction() == Constraint.SET_NULL) {
                            for (int j = 0; j < r_columns.length; j++) {
                                rnd[r_columns[j]] = null;
                            }
                        } else {
                            for (int j = 0; j < r_columns.length; j++) {
                                Column col = reftable.getColumn(r_columns[j]);

                                rnd[r_columns[j]] =
                                    col.getDefaultValue(session);
                            }
                        }

                        if (hasref && path.add(c)) {

                            // fredt - avoid infinite recursion on circular references
                            // these can be rings of two or more mutually dependent tables
                            // so only one visit per constraint is allowed
                            checkCascadeUpdate(session, reftable, null,
                                               refrow, rnd, r_columns, null,
                                               path);
                            path.remove(c);
                        }

                        if (delete) {

                            //  foreign key referencing own table - do not update the row to be deleted
                            if (reftable != table || !refrow.equals(row)) {
                                mergeUpdate(rowSet, refrow, rnd, r_columns);
                            }
                        }
                    } else if (hasref) {
                        if (reftable != table) {
                            if (path.add(c)) {
                                checkCascadeDelete(session, reftable,
                                                   tableUpdateLists, refrow,
                                                   delete, path);
                                path.remove(c);
                            }
                        } else {

                            // fredt - we avoid infinite recursion on the fk's referencing the same table
                            // but chained rows can result in very deep recursion and StackOverflowError
                            if (refrow != row) {
                                checkCascadeDelete(session, reftable,
                                                   tableUpdateLists, refrow,
                                                   delete, path);
                            }
                        }
                    }

                    if (delete && !isUpdate && !refrow.isCascadeDeleted()) {
                        reftable.deleteNoRefCheck(session, refrow);
                    }
                }
            } finally {
                refiterator.release();
            }
        }
    }