Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions Sources/Db/Schema/Table.php
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,10 @@ public function populate(bool $replace = false): int

$num_inserts = \count($ids ?? $this->initial_data);

if (isset($auto_col)) {
$this->resyncAutoIncrement($auto_col);
}

return $num_inserts;
}

Expand Down Expand Up @@ -697,4 +701,46 @@ final public static function getInitializers(string $schema_version): array

return [];
}

/******************
* Internal methods
******************/

/**
* Points the table's ID generator past the rows that were just inserted.
*
* The initial data carries its own IDs — the default board is board 1, and
* plenty of other rows are referred to by number elsewhere in it — so they
* are supplied rather than generated.
*
* MySQL notices that and moves AUTO_INCREMENT along by itself. PostgreSQL
* does not: a sequence only advances when something calls nextval() on it,
* and nothing has, so it still sits at 1 and hands 1 to the next insert.
* That collides with the row already there, and the insert fails on the
* primary key. The first topic anybody starts on a new forum is the usual
* way to meet this, and it fails once and then works, because the failed
* attempt consumed the 1 and the retry gets 2.
*
* The 2.1 upgrade path has had this fix for years, in the PostgreSqlSequences
* migration. This is the same thing for a fresh install.
*
* @param string $auto_col Name of the auto-incrementing column.
*/
private function resyncAutoIncrement(string $auto_col): void
{
if (Db::$db->title !== POSTGRE_TITLE) {
return;
}

// COALESCE for the table that ended up empty after an 'ignore' insert:
// setval() will not accept NULL, and 1 is where the sequence began.
Db::$db->query(
'SELECT setval(\'{raw:sequence}\', COALESCE((SELECT MAX({raw:column}) FROM {db_prefix}{raw:table}), 1))',
[
'sequence' => Db::$db->prefix . $this->name . '_seq',
'column' => $auto_col,
'table' => $this->name,
],
);
}
}
Loading