Прервать MySQL скрипт условно - PullRequest
2 голосов
/ 06 января 2010

Дано: база данных MySQL. Иногда изменения схемы базы данных и обновления выкатываются (в форме сценария sql). Чтобы гарантировать правильный порядок применения обновлений (без повторяющихся обновлений, без обновлений и т. Д.), Я планирую развернуть следующее решение:

  • CREATE TABLE meta (имя TEXT PRIMARY KEY, val TEXT);
  • INSERT INTO meta VALUES ('version', '0');

Каждый скрипт обновления содержит версию N, которая назначается последовательно. Перед выполнением обновлений скрипт проверяет, соответствует ли meta.version предыдущей версии скрипта N-1. После выполнения обновлений meta.version обновляется до N. Мне не нужно защищать от нескольких параллельно работающих скриптов.

Вопрос: как проверить версию и отменить скрипт, если он не совпадает? Я понял, что выполнение

CALL `raise error`

сломает скрипт, но как его выполнить условно в зависимости от meta.version? Хранимые процедуры не допускаются. Значимое сообщение об ошибке является плюсом. Это не обеспечивает подходящего решения.

Ответы [ 2 ]

1 голос
/ 11 января 2010

Хорошо, новый ответ. Дело не в том, что щедрость делает меня более креативным, скорее я чувствую себя менее сдержанно, предлагая решение I , не считаю прямым:)

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

Решение № 1: с использованием подготовленных заявлений

Предположим, ваш сценарий обновления прост:

ALTER TABLE t ADD COLUMN c INT DEFAULT 1

Тогда как насчет этого:

SET @version := '1';

SELECT CASE val
           WHEN @version THEN 
               'ALTER TABLE t ADD COLUMN c INT DEFAULT 1'
           ELSE 'SELECT ''Wrong version. Nothing to upgrade.'''
       END
INTO   @stmt
FROM   meta 
WHERE  name = 'Version';

PREPARE stmt FROM @stmt;
EXECUTE stmt;

Очевидные недостатки:

  • Разбейте беспорядок и накладные расходы, поскольку вы должны написать это для каждого отдельного оператора (PREPARE не может пакетировать несколько операторов)
  • дополнительное раздражение, потому что вы должны записывать каждое утверждение из вашего скрипта обновления в виде строки ... yuck
  • не все операторы могут быть написаны и выполнены с PREPARE

Решение № 2: один подготовленный оператор, чтобы УБИТЬ соединение

Вы уже указали, что вам не нравится решение, которое вы тоже связали ... это из-за оператора KILL или из-за сохраненной функции? Если это из-за сохраненной функции, вы можете обойти это:

SET @version := '1';

SELECT CASE val 
           WHEN @version THEN 'SELECT ''Performing upgrade...'''
           ELSE CONCAT('KILL CONNECTION', connection_id())
       END
INTO   @stmt
FROM   meta
WHERE  name = 'Version';

PREPARE stmt FROM @stmt;
EXECUTE stmt;  -- connection is killed if the version is not correct.

-- remainder of the upgrade script goes here...
  • mysql необходимо запустить с параметром --skip-reconnect, чтобы гарантировать, что никакие операторы не могут быть выполнены, если соединение разорвано

Я попытался обойти этот недостаток и использовал PREPARE для других вещей, которые блокировали бы текущее соединение, таких как удаление пользователя и отзыв его привилегий. К сожалению, это не работает как задумано (нет, также не после FLUSH PRIVILEGES)

Решение № 3: использовать прокси-сервер mysql для перехвата

Еще одно решение: использовать прокси. С помощью прокси-сервера mysql (см .: http://forge.mysql.com/wiki/MySQL_Proxy), вы можете написать собственный сценарий перехвата в lua, который интерпретирует ваши пользовательские команды. С его помощью вы можете добавить необходимые управляющие структуры. Недостаток: теперь вам нужно создать свой собственный мини-язык, и научись Луа интерпретировать это:)

1 голос
/ 06 января 2010

Я думаю, что это лучше всего решить с помощью сценария оболочки или приложения-установщика. У вас не может быть никаких управляющих конструкций в скрипте SQL.

Очень простым решением было бы создать сценарий, который генерирует командную строку для фактического выполнения сценария на основе выбора мета таблицы.

Допустим, у вас есть этот скрипт, gen_upgrade_command.sql:

select concat(
       'mysql -u<user> -p<password> -h<host> -e"SOURCE upgrade'
   ,    val + 1
   ,   '.sql"'
   )
from meta;

Который ты так бегаешь

mysql -u<user> -p<password> -h<host> -Nrs < gen_upgrade_command.sql > do_upgrade.bat

Теперь do_upgrade.bat содержит эту сгенерированную командную строку:

mysql -u<usere> -p<password> -h<host> -e"SOURCE upgrade1.sql"

и работает do_upgrade.bat будет работать upgrade1.sql

Конечно, вы можете изменить исходный скрипт, чтобы вообще не выбирать ни одной строки, это только ваше дело.

...