docs: static void checkCascadeDelete(Session session, Table table,
                                     HashMappedList tableUpdateList, Row row,
                                     boolean delete, HashSet path) {
  
          for (int i = 0, size = table.fkMainConstraints.length; i < size; i++) {
              Constraint c = table.fkMainConstraints[i];
              RowIterator refiterator = c.findFkRef(session, row.getData(),
                                                    delete);
  
              if (!refiterator.hasNext()) {
                  continue;
              }
  
              try {
                  if (c.core.deleteAction == Constraint.NO_ACTION
                          || c.core.deleteAction == Constraint.RESTRICT) {
                      if (c.core.mainTable == c.core.refTable) {
                          Row refrow = refiterator.getNextRow();
  
                          // 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;
                          }
                      }
  
                      int errorCode = c.core.deleteAction
                                      == Constraint.NO_ACTION ? ErrorCode.X_23501
                                                              : ErrorCode
                                                                  .X_23001;
                      String[] info = new String[] {
                          c.core.refName.name, c.core.refTable.getName().name
                      };
  
                      throw Error.error(errorCode, ErrorCode.CONSTRAINT, info);
                  }
  
                  Table reftable = c.getRef();
  
                  // shortcut when deltable has no imported constraint
                  boolean hasref = reftable.fkMainConstraints.length > 0;
  
                  // if (reftable == this) we don't need to go further and can return ??
                  if (!delete && !hasref) {
                      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) tableUpdateList.get(reftable);
  
                      if (rowSet == null) {
                          rowSet = new HashMappedList();
  
                          tableUpdateList.add(reftable, rowSet);
                      }
                  }
  
                  // walk the index for all the nodes that reference delnode
                  for (;;) {
                      Row refrow = refiterator.getNextRow();
  
                      if (refrow == null || refrow.isDeleted(session)
                              || refindex.compareRowNonUnique(
                                  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++) {
                                  ColumnSchema 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,
                                                     tableUpdateList, 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,
                                                     tableUpdateList, refrow,
                                                     delete, path);
                              }
                          }
                      }
  
                      if (delete && !isUpdate && !refrow.isDeleted(session)) {
                          reftable.deleteRowAsTriggeredAction(session, refrow);
                      }
                  }
              } finally {
                  refiterator.release();
              }
          }
      }