Skip to content
Merged
Show file tree
Hide file tree
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
8 changes: 8 additions & 0 deletions src/bundle/Core/Resources/config/doctrine_migrations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,11 @@ services:
$connection: '@ibexa.persistence.connection'
tags:
- { name: !php/const Ibexa\Contracts\DoctrineMigrations\Migrations\IbexaMigrationTag::TAG }

Ibexa\Bundle\RepositoryInstaller\Migration\ConvertPostgreSqlSerialColumnsToIdentityMigration:
autowire: true
public: false
arguments:
$connection: '@ibexa.persistence.connection'
tags:
- { name: !php/const Ibexa\Contracts\DoctrineMigrations\Migrations\IbexaMigrationTag::TAG }
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Bundle\RepositoryInstaller\Migration;

use DateTimeImmutable;
use Doctrine\DBAL\Schema\Schema;
use Ibexa\Contracts\DoctrineMigrations\Migrations\AbstractSqlMigration;
use Ibexa\Contracts\DoctrineMigrations\Migrations\IbexaMigrationInterface;
use Ibexa\Contracts\DoctrineMigrations\Migrations\SqlPlatform;

/**
* Turns the SERIAL columns of the core tables into identity columns on PostgreSQL, as a fresh 6.0
* install creates them.
*
* Doctrine DBAL 4 creates an autoincrement column on PostgreSQL as GENERATED BY DEFAULT AS
* IDENTITY, where DBAL 2 and 3 used SERIAL: a column defaulting to the next value of its own
* sequence. The install migrations, which a 6.0 install runs too, are the ones from 4.6 and 5.0, so
* they created these as SERIAL, and a database upgraded from 5.0 keeps them.
* sql/convert-serial-columns-to-identity-postgresql.sql turns each SERIAL column into an identity
* column that carries on from its sequence's current value, drops the old sequence, and skips a
* column that's no longer SERIAL.
*
* This is kept apart from the other 6.0 migrations on purpose: SERIAL columns work the same for
* inserts, so leaving this one out keeps them as they are.
*/
final class ConvertPostgreSqlSerialColumnsToIdentityMigration extends AbstractSqlMigration implements IbexaMigrationInterface
{
public function getDescription(): string
{
return 'Turns the core SERIAL columns into identity columns on PostgreSQL';
}

public static function getTargetVersion(): string
{
return '6.0.0';
}

public static function getCreationDate(): DateTimeImmutable
{
return new DateTimeImmutable('2026-10-06 00:00:03');
}

public function up(Schema $schema): void
{
$this->abortIfUnsupportedPlatform(SqlPlatform::MYSQL, SqlPlatform::MARIADB, SqlPlatform::POSTGRESQL, SqlPlatform::SQLITE);

if ($this->isPostgreSQL()) {
$this->addSqlFile(__DIR__ . '/sql/convert-serial-columns-to-identity-postgresql.sql');
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
DO $$
DECLARE
serial_column record;
sequence_name text;
old_last_value bigint;
old_is_called boolean;
BEGIN
FOR serial_column IN SELECT * FROM (VALUES
('ibexa_content', 'id'),
('ibexa_content_bookmark', 'id'),
('ibexa_content_field', 'id'),
('ibexa_content_relation', 'id'),
('ibexa_content_tree', 'node_id'),
('ibexa_content_type', 'id'),
('ibexa_content_type_field_definition', 'id'),
('ibexa_content_type_group', 'id'),
('ibexa_content_version', 'id'),
('ibexa_image_file', 'id'),
('ibexa_keyword', 'id'),
('ibexa_keyword_field_link', 'id'),
('ibexa_node_assignment', 'id'),
('ibexa_notification', 'id'),
('ibexa_object_state', 'id'),
('ibexa_object_state_group', 'id'),
('ibexa_package', 'id'),
('ibexa_policy', 'id'),
('ibexa_policy_limitation', 'id'),
('ibexa_policy_limitation_value', 'id'),
('ibexa_role', 'id'),
('ibexa_search_object_word_link', 'id'),
('ibexa_search_word', 'id'),
('ibexa_section', 'id'),
('ibexa_setting', 'id'),
('ibexa_token', 'id'),
('ibexa_token_type', 'id'),
('ibexa_url', 'id'),
('ibexa_url_alias', 'id'),
('ibexa_url_alias_ml_incr', 'id'),
('ibexa_url_wildcard', 'id'),
('ibexa_user_accountkey', 'id'),
('ibexa_user_preference', 'id'),
('ibexa_user_role', 'id')
) AS c (table_name, column_name)
LOOP
-- A SERIAL column defaults to nextval('<sequence>'::regclass); an identity column has no default.
SELECT substring(column_default FROM 'nextval\(''([^'']+)''::regclass\)') INTO sequence_name
FROM information_schema.columns
WHERE table_schema = current_schema()
AND table_name = serial_column.table_name
AND column_name = serial_column.column_name;
CONTINUE WHEN sequence_name IS NULL;

EXECUTE format('SELECT last_value, is_called FROM %s', sequence_name) INTO old_last_value, old_is_called;
EXECUTE format('ALTER TABLE %I ALTER %I DROP DEFAULT', serial_column.table_name, serial_column.column_name);
EXECUTE format('DROP SEQUENCE %s', sequence_name);
EXECUTE format('ALTER TABLE %I ALTER %I ADD GENERATED BY DEFAULT AS IDENTITY', serial_column.table_name, serial_column.column_name);
PERFORM setval(pg_get_serial_sequence(serial_column.table_name, serial_column.column_name), old_last_value, old_is_called);
END LOOP;
END
$$;
Loading