image
Бекапы это хорошо, а еще лучше когда они работают так как надо когда они нужны. На одном из проектов понадобилось восстановить дамп из 745 триггеров и накатить их на рабочую MySQL базу.

MySQL позволяет использовать любые имена триггеров, в том числе с использованием точек (например: analitica.cron.indeg.y.run.a_insert). А mysqldump при создании дампа не учитывает это обстоятельство и добавляет конструкцию для их дропа следующего вида:

/*!50032 DROP TRIGGER IF EXISTS analitica.cron.indeg.y.run.a_insert */;

Подвох ожидает при попытке эти дампы накатить на базу где эти триггеры уже созданы. С точки зрения MySQL этот запрос не дропнет триггер, потому что не найдет триггер с таким именем. Для корректной работы имя триггера должно быть заключено в апострофы.

В попытках найти обходное решение зарепортил соответствующий репорт в баг трекер Перконы, а они уже продублировали аналогичный репорт в официальный трекер MySQL.

Поскольку исправлять этот баг будут долго, а дампы мне нужны прямой сейчас. Решить эту задачу я решил самым прямолинейным способом, а именно самостоятельно пропатчив mysqldump. Для этого склонировал официальный репозиторий перконовского дитрибутива MySQL 5.6 с GitHub'а.

git clone --recursive --depth 1 https://github.com/percona/percona-server/

Открыл файл client/mysqldump.c и добавил апострофы к конструкции DROP TRIGGER IF EXISTS в паре мест. Если посмотреть diff, то получается такой патч:

@@ -3517,7 +3517,7 @@ static void dump_trigger_old(FILE *sql_file, MYSQL_RES *show_triggers_rs,
     fprintf(sql_file, "/*!50003 SET @OLD_SQL_MODE=@@SQL_MODE*/;\n");

   if (opt_drop_trigger)
-    fprintf(sql_file, "/*!50032 DROP TRIGGER IF EXISTS %s */;\n", (*show_trigger_row)[0]);
+    fprintf(sql_file, "/*!50032 DROP TRIGGER IF EXISTS `%s` */;\n", (*show_trigger_row)[0]);

   fprintf(sql_file,
           "DELIMITER ;;\n"
@@ -3604,7 +3604,7 @@ static int dump_trigger(FILE *sql_file, MYSQL_RES *show_create_trigger_rs,
     switch_sql_mode(sql_file, ";", row[1]);

     if (opt_drop_trigger)
-      fprintf(sql_file, "/*!50032 DROP TRIGGER IF EXISTS %s */;\n", row[0]);
+      fprintf(sql_file, "/*!50032 DROP TRIGGER IF EXISTS `%s` */;\n", row[0]);

Чтобы собрать пропатченую версию надо поставить пару пакетов и на Ubuntu/Debian, запустить cmake с параметрами из документации к Перконе, а затем make'ом собрать только mysqldump.

apt-get install build-essential cmake bison libaio-dev libncurses5-dev libreadline-dev
cmake . -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_CONFIG=mysql_release -DFEATURE_SET=community -DWITH_EMBEDDED_SERVER=OFF
make mysqldump

После компиляции получаем патченую версию mysqldump, в которой имена триггеров экранируются корректно. Можно дампить новым дампером:

./percona-server/client/mysqldump     --socket=/var/run/mysqld/mysqld.sock     -uroot -p --routines --events --triggers     --add-drop-trigger --quote-names     --no-create-info --no-data --no-create-db --skip-opt     database_name | sed -r 's/DEFINER[ ]*=[ ]*[^*]*\*/\*/' > dump.sql

Комментарии (9)


  1. kloppspb
    09.10.2015 18:52
    +8

    > пропатчив mysqldump

    $ mysqldump | perl -lne 's{(DROP TRIGGER IF EXISTS) (\S+) \*/}{$1 `$2` \*/};print'
    


    Ну или что-то в этом роде :)


    1. dotZero
      09.10.2015 18:56

      Это слишком просто :)


      1. kloppspb
        09.10.2015 19:32

        Зато быстро :)

        BTW, ещё мысль тут же промелькнула, ну, чтобы не ждать патча :) Если надо только триггеры, то можно их выцепить через банальное «show triggers», оно кавычит нормально. Вроде бы…


        1. khim
          10.10.2015 21:49
          +1

          Может и быстро, но неправильно: если в базе встретится вот эта вот статья, то после восстановления из backup'а в ней станет несколько больше апострофов, что не есть карашо.


          1. kloppspb
            10.10.2015 22:12

            «или что-то в этом роде» предполагает, что регексп писался на коленке только для демонстрации идеи. Немного раздумий и он правится до правильного состояния.

            Впрочем, поклоннкам картинки в комменте ниже, любое движение в этом направлении рекомендовать не буду.


    1. ivlis
      09.10.2015 18:59
      +5

      Без этой картинки комментарий будет не полным
      image


  1. xonix
    10.10.2015 04:08
    +1

    sed -r 's/DEFINER[ ]*=[ ]*[^*]*\*/\*/'

    Может это тоже стоит пропатчить…


  1. kingu
    10.10.2015 14:09
    +1

    любые имена триггеров, в том числе с использованием точек

    Зачем использовать заведомо проблемные имена?


    1. dotZero
      10.10.2015 15:40
      +1

      Их писал человек пришедший из Postgre там это не было проблемой.