I had this exact issue and after manually deleting several of the duplicates, I wrote a patch in the lib/grade/grade_object.php file to delete the duplicates as it finds them. It's kind of a hack, but it has worked each time I've done an upgrade and prevents the constant stopping during the assignment module upgrade. I have attached the file. Each line I added has a comment with "KC".
Note: I used a global variable to store the $sql, but I'm sure there's a better way. This was just a quick fix, but you get the idea.
These are the two functions that were changed:
/**
- Factory method - uses the parameters to retrieve matching instance from the DB.
- @static final protected
- @return mixed object insatnce or false if not found
*/
function fetch_helper($table, $classname, $params) {
// we have to do use this hack because of the incomplete OOP implementation in PHP4 
// in PHP5 we could do it much better
if ($instances = grade_object::fetch_all_helper($table, $classname, $params)) {
if (count($instances) > 1) {
// we should not tolerate any errors here - problems might appear later
global $sql_delete; //KC added
echo $sql_delete; //KC added
$result_delete = mysql_query($sql_delete) or die(mysql_error()); //KC instead of saying there's an error, it deletes the duplicate and continues
//error('Found more than one record in fetch() !');
}
return reset($instances);
} else {
return false;
}
}
/**
* Factory method - uses the parameters to retrieve all matching instances from the DB.
* @static final protected
* @return mixed array of object instances or false if not found
*/
function fetch_all_helper($table, $classname, $params) {
// we have to do use this hack because of the incomplete OOP implementation in PHP4 
// in PHP5 we could do it much better
$instance = new $classname();
$classvars = (array)$instance;
$params = (array)$params;
$wheresql = array();
// remove incorrect params
foreach ($params as $var=>$value) {
if (!in_array($var, $instance->required_fields) and !array_key_exists($var, $instance->optional_fields)) {
continue;
}
if (is_null($value)) {
$wheresql[] = " $var IS NULL ";
} else {
$value = addslashes($value);
$wheresql[] = " $var = '$value' ";
}
}
if (empty($wheresql)) {
$wheresql = '';
} else {
$wheresql = implode("AND", $wheresql);
}
if ($datas = get_records_select($table, $wheresql, 'id')) {
$result = array();
foreach($datas as $data) {
$instance = new $classname();
grade_object::set_properties($instance, $data);
$result[$instance->id] = $instance;
}
global $sql_delete; //KC added
//Added by KC 10/11/07 to fix upgrade duplication issue
$sql_delete = "Delete from mdl_" . $table . " where $wheresql LIMIT 1";
return $result;
} else { return false; } }
}
I turned debug on in /mod/assignment/upgrade.php and started this again, now get:
SELECT * FROM mdl_grade_grades WHERE itemid = '191' AND userid = '3751' ORDER BY id
Found more than one record in fetch() !
When I pull up mdl_grade_grades, I find two entries that meet this criteria.
I deleted the entry with the higher id and started again, got the error again - same itemid, different userid. Went into database and found several duplicates for itemid = 191, deleted the second entry on each duplicate.
Ran again, same error this time, but itemid = 190.
Continuing process - I'll comment back how it goes.
The only extra factor I can think of in this is that we ARE using GBPV2.