Liquibase не учитывает RAISERROR SQL server

#sql-server #liquibase

#sql-сервер #liquibase

Вопрос:

У нас есть несколько сценариев настройки, которые немного сложны. Эти сценарии используют ряд доступных хранимых процедур в нашей базе данных для вставки данных конфигурации. Если какой-либо скрипт попытается вставить недопустимые данные, хранимые процедуры вызовут

 RAISERROR @msg, 16, 1
  

И тогда SP вернется. SP запускают свои собственные именованные транзакции, и они будут откатывать / фиксировать именованную транзакцию, однако LB не обнаруживает возникшую ошибку и принимает выполнение как успешное выполнение. Но мы не хотим продолжать, если это произойдет.

failOnError в наборах изменений имеет значение true, но LB все равно продолжается. Даже в DATABASECHANGELOG этот неудачный набор изменений помечен как выполненный, как если бы он был успешным.

Мы также пытались удалить вложенные транзакции (именованные транзакции), но безуспешно.

Мы удалили имя из транзакции и просто использовали BEGIN TRAN, выполнение LB останавливается при неправильном сценарии, но проблема в том, что LB не может зафиксировать свою собственную транзакцию и не может снять блокировку, поэтому она остается ЗАБЛОКИРОВАННОЙ.

Есть ли способ сообщить LB, что произошла ошибка, и остановить ее?

Мы используем Liquibase 3.5.0. и Microsoft SQL Server

=== РЕДАКТИРОВАТЬ

Итак, после отладки Liquibase мы обнаружили две вещи:

  1. При подключении к MS SQL Server, если возникает RAISERROR и в скрипте также есть результирующие наборы, он не выдаст исключение, если мы не вызовем statement.getMoreResults() . То же самое происходит с Sybase (мы тестировали это и с Sybase). Поэтому мы подумали, что, возможно, в LB после выполнения инструкции нам нужно вызвать getMoreResults(), пока он не выдаст исключение или не вернет false, что означает, что ошибки не произошло.

  2. Скрипт выполняет вызов хранимой процедуры. Хранимая процедура имеет «BEGIN TRAN», и в конце она либо ФИКСИРУЕТСЯ, либо ОТКАТЫВАЕТСЯ. Если происходит откат, он также выполняет RAISERROR. Обратите внимание, что наши скрипты не выполняют никаких обновлений / вставок, они только предоставляют данные во временной таблице, поэтому мы не выполняем обработку транзакций в наших скриптах. В этом сценарии, учтите, что мы добавили код для вызова getMoreResults() , исключение выдается правильно, но затем в LB исполнитель пытается database.rollback(), а затем снова в StandardLockService, прежде чем снять блокировку, он пытается database.rollback(), который заканчивается исключениемпотому что наш SP уже откатил транзакцию. Этот последний откат в LB приводит к тому, что ошибка, вызванная JDBC, проглатывается, и в результате мы не только видим ошибку, которая ее вызвала, но и блокировку, которая осталась неизданной, и это вызывает наибольшую озабоченность, потому что, даже если мы повторно запустим скрипт и исправим его, блокировка не была снятаи нам нужно сделать это вручную.

Можно возразить, что наша обработка транзакций неверна, но все, что я пытаюсь сказать, это то, что снятие блокировки не должно влиять, если наш сценарий неверен. LB должен снимать блокировку и вызывать исключение или продолжить, если сценарий / набор изменений не выполняется успешно.

Комментарии:

1. Я также отметил это в очень простых скриптах, которые выполняют всего несколько вставок. Решением для меня было разделить каждую вставку разделителем пакетов (GO). Я использую sql в формате Liquibase.

2. Насколько мне известно, Liquibase использует соединение JDBC с БД. Есть ли у вас под рукой инструмент для работы с БД, который также использует драйвер JDBC? Вы можете вручную запустить инструкции SQL, которые будет выполнять liquibase, и посмотреть, что произойдет. Может быть, ошибка не передается обратно в инструкцию sql, которая вызвала SP? И, следовательно, не сообщается через JDBC вызывающему коду? Я ничего не знаю о SQL-Server dbs. Это просто подсказка о том, как вы могли бы сузить причину этого…

3. @PeterHenell Наш скрипт не отформатирован в формате LB. Многие из них перемещаются из устаревшей системы. Они выполняют несколько действий, например, вставляют записи, проводят дюжину проверок зависимостей, чтобы убедиться, что не вставлены недопустимые данные и т.д.

4. @Jens Я не запускал их с помощью инструмента JDBC, но если вы запустите тот же скрипт в Salford server studio, он распечатает журнал красным цветом, поскольку произошла ошибка. Я попробую использовать JDBC, когда вернусь к работе

5. Если вы используете SQL Server 2012 или новее, вы можете попробовать THROW команду, которая прерывает пакетную обработку, в то время RAISERROR как это не так. В противном случае вам может потребоваться перехватить и проверить сообщения, возвращающиеся с SQL Server. Обычно это просто операторы печати, но в зависимости от настроек драйвера / подключения ошибки могут обрабатываться как сообщения. В этом случае вам необходимо перехватывать сообщения и проверять их «состояние» / «серьезность» / «уровень», чтобы определить, являются ли они ошибочными или информационными.

Ответ №1:

Если кто-нибудь тоже сталкивается с этим: в моем случае у меня был очень сложный SQL-скрипт только для MS SQL Server. Это также не смогло остановить выполнение изменений LB, если в сценарии SQL произошла ошибка, в любом случае, если я использую RAISERROR или THROW. Что мне нужно сделать, чтобы заставить его работать:

  • удалите (или прокомментируйте) все места, где были созданы результирующие наборы (ВЫБЕРИТЕ)
  • запустите SQL-скрипт с «SET NOCOUNT ON;», чтобы избежать результатов вставки или обновления (… затронутых строк)
  • завершите SQL-скрипт с помощью «SET NOCOUNT OFF;», чтобы LB мог работать должным образом сразу после выполнения SQL-скрипта (set EXECTYPE)

Ответ №2:

Используйте предварительное условие https://docs .liquibase.com/concepts/advanced/preconditions.html

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