Дублирование имен индексов с помощью DBIx::Class и PostgreSQL

У меня есть существующая база данных MySQL, которую я пытаюсь перенести на PostgreSQL, выполнив следующие действия. База данных довольно проста — у нее есть несколько внешних ключей и других ограничений, но нет триггеров, процедур и т. д.

  1. Используйте DBIx::Class::Schema::Loader для создания набора Result классов из существующей базы данных MySQL.
  2. Используйте классы Result для создания набора операторов CREATE TABLE для PostgreSQL.
  3. Запустите операторы CREATE TABLE, используя psql, чтобы настроить таблицы (я еще не дошел до импорта данных).

Сценарий, который я использую, выглядит следующим образом (с удаленными учетными данными и именем БД):

#!/usr/bin/perl

use Modern::Perl;
use DBIx::Class::Schema::Loader qw/ make_schema_at /;

my $dsn = 'dbi:mysql:dbname=database';
my $user = '';
my $pass = '';

make_schema_at(
  'MyDB::Schema',
  { debug => 1, dump_directory => './lib' },
  [ $dsn, $user, $pass
  ],
);

my $schema = MyDB::Schema->connect($dsn, $user, $pass);
$schema->create_ddl_dir(['PostgreSQL'], '0.1', './', undef, { add_drop_table => 0 });

Сценарий выполняется успешно, и классы Result, и файл .sql (содержащий все операторы CREATE TABLE) выглядят так, как я и ожидал.

Однако в некоторых таблицах есть столбец slug, который помечен как UNIQUE в исходной схеме MySQL и приводит к следующим строкам как часть оператора CREATE TABLE:

"slug" character varying(50) NOT NULL,
CONSTRAINT "slug" UNIQUE ("slug")

Когда я пытаюсь импортировать данные (используя psql < tables.sql), я получаю следующую ошибку в каждой таблице с уникальным столбцом slug после первого:

NOTICE:  CREATE TABLE / UNIQUE will create implicit index "slug" for table "mytable"
ERROR:  relation "slug" already exists

Насколько я понимаю, имена индексов должны быть уникальными в данной базе данных. У меня нет этой проблемы с MySQL, так как я просто объявляю slug VARCHAR(50) NOT NULL UNIQUE без указания имени индекса.

Есть ли способ заставить DBIx::Class (или SQL::Translator, который использует функция create_ddl_dir) генерировать уникальное имя индекса в выходных данных? Мне все равно, как называются индексы, хотя что-то, основанное на имени таблицы, было бы разумно. Я просмотрел документацию, но не вижу никаких параметров, позволяющих это сделать.

Я мог бы просто отредактировать каждое ограничение вручную, прежде чем импортировать файл .sql, но там более 250 таблиц и множество коллизий, к тому же мне приходилось бы делать это каждый раз, когда я настраивал процесс миграции и заново создавал классы и SQL.


person pwaring    schedule 16.04.2013    source источник
comment
Вы пытались запустить SQL::Translator в своей схеме MySQL напрямую, минуя DBIx::Class::Schema::Loader?   -  person nwellnhof    schedule 17.04.2013
comment
Та же проблема - он выводит один и тот же SQL, что приводит к дублированию имен индексов.   -  person pwaring    schedule 18.04.2013
comment
Кажется, вы создали именованные уникальные ограничения в своей схеме MySQL. Должно работать использование безымянных ограничений простым использованием UNIQUE в определении столбца.   -  person nwellnhof    schedule 25.04.2013
comment
Вот как я создал ограничения в MySQL (то есть просто используя UNIQUE), однако, когда вы берете дамп базы данных и передаете его через SQL::Translator, он генерирует именованные уникальные ограничения.   -  person pwaring    schedule 26.04.2013


Ответы (1)


В конце концов, единственный автоматизированный способ исправить это был с помощью Perl-скрипта, который заставлял каждое имя ограничения (и, следовательно, автоматический индекс) быть уникальным, анализируя вывод из SQL::Translator и добавляя _$i к каждому имени, где $i увеличивается каждый раз, когда видно новое ограничение.

Код выглядит следующим образом - предполагается, что вы передадите схему на STDIN:

#!/usr/bin/perl

use Modern::Perl;

my $i = 1;

while (my $line = <>)
{
  if ($line =~ m/CONSTRAINT\s+"([a-zA-Z_]+)"\s+UNIQUE\s+\("([a-zA-Z_]+)"\)/)
  {    
    my $constraint_name = $1 . '_' . $i;
    my $column_name = $2;

    my $replace_str = 'CONSTRAINT "' . $constraint_name . '" UNIQUE ("' . $column_name . '")';

    $line =~ s/CONSTRAINT\s+"([a-zA-Z_]+)"\s+UNIQUE\s+\("([a-zA-Z_]+)"\)/$replace_str/;
    $i++;
  }

  print $line;
}
person pwaring    schedule 26.04.2013