Doctrine 3 et Postgres : passage des sequences aux identity columns
Auteur : Philippe Le Van - @plv@framapiaf.org
Date : 30 octobre 2025
Contexte
On part d’un projet Symfony avec une base Postgresql.
En changeant de version de symfony (passage en Symfony 6.x à 7.3) et
de version de Doctrine (2.x vers 3.4), Doctrine nous suggère de passer de
la gestion des id par sequence en gestion des id par identity.
Pour une table "template", ça nous donne la migration suivante :
|  | $this->addSql('ALTER TABLE template ALTER id TYPE INT');
$this->addSql('ALTER TABLE template ALTER id ADD GENERATED BY DEFAULT AS IDENTITY');
 | 
La gestion des id par des identity column, en fait, c’est juste une sequence cachée.
L’ancienne séquence s’appelait template_id_seq et la nouvelle template_id_seq1
Le problème, c’est que la nouvelle séquence repart à 1.
Requêtes utiles
|  | -- connaitre la valeur d’une séquence :
SELECT last_value FROM template_id_seq1;
-- autre solution. WARNING, ça incrémente la séquence.
select nextval('template_id_seq1')
 | 
Mettre à jour la séquence cachée
Solution avec alter table
|  | select max(id) from template;
-- donne 34
ALTER TABLE template ALTER id RESTART 35;
-- upgrade la séquence
 | 
Solution avec setval
On peut faire l’opération en une seule requête en utilisant la fonction
setval.
Par contre, ça suppose de connaître le nom de la séquence cachée.
|  | SELECT setval('template_id_seq1', (SELECT MAX(id) FROM template), true);
 | 
Changer les séquence pour toutes les tables
Sur notre projet Symfony, nous n’avons pas beaucoup de tables. Nous pouvons faire
les changements "à la main". Dans notre cas, nous avons fait une boucle en bash avec
la liste de toutes les tables.
|  | for ta in activity checklist checklist_line checklist_template checklist_template_line company document messenger_messages organization template template_file user
do 
  echo "========== TABLE $ta ==========="
  ./bin/console doctrine:query:sql "SELECT setval('${ta}_id_seq1', (SELECT MAX(id) FROM \"${ta}\"), true);"
done
 | 
Mais notre table messenger_messages est vide, du coup le SELECT MAX(id)... est
vide aussi. On initialise le compteur à 1 pour cette table.
|  | ./bin/console doctrine:query:sql "SELECT setval('messenger_messages_id_seq1', 1, true);"
 | 
Ces modifications ont bien réglé le problème sur notre projet.
Migrations symfony
On n’avait pas anticipé le problème, on a dû le résoudre en prod, mais idéalement,
il aurait fallu faire une migration :
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 | <?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
 * Auto-generated Migration: Please modify to your needs!
 */
final class Version20251030072618 extends AbstractMigration
{
    public function getDescription(): string
    {
        return '';
    }
    public function up(Schema $schema): void
    {
        // this up() migration is auto-generated, please modify it to your needs
        $tables = [
            "activity",
            "checklist",
            "checklist_line",
            "checklist_template",
            "checklist_template_line",
            "company",
            "document",
            "organization",
            "template",
            "template_file",
            "user",
        ];
        foreach ($tables as $table) {
            $this->addSql(sprintf(
                "SELECT setval('%s_id_seq1', (SELECT MAX(id) FROM \"%s\"), true);",
                $table,
                $table
            ));
        }
        $this->addSql("SELECT setval('messenger_messages_id_seq1', 1, true);");
    }
    public function down(Schema $schema): void
    {
    }
}
 |