InterBase: тормозология и глюконавтика
Страница 21. Как запороть базу средствами SQL


Как запороть базу средствами SQL

И так, существуют вещи, которые в InterBase можно сделать одними конструкциями SQL, но которые с точки зрения других конструкций или средств (а так же по жизни) являются некорректными. Так, мне приходилось наблюдать, как не удавалось создать ссылочное ограничение, которое ссылалось на поле, по которому было множество разных индексов. Это притом, что уже существовали ссылочные ограничения, созданные до индексов. Когда часть индексов была уничтожена, ссылочное ограничение создалось нормально. Причём в простых случаях этот эффект промоделировать не удалось. Могу сказать лишь, что ссылок было много, индексов было много и часть из них дублировалась, то есть, были индексы asсending и descending под одним и тем же наборам полей.

Особая песня - добавление полей типа not null. Представим себе, что в таблице уже есть данные, и мы добавляем такое поле. Естественно, что существующие записи будут расширены какими-то значениями. Но какими? Null использовать формально нельзя. По логике нужно применить значение default, если оно есть, а если нет - то отменить операцию, как некорректную. Вот только InterBase добавляет поля всегда в состоянии null, в нарушение всяких ограничений и никогда не использует default.

Причём, если в дальнейшем вы захотите это поле прочитать, то прочитается именно null, а если обновить, то будет ругань о том, что пустое значение недопустимо. И так будет даже тогда, когда обновляется другое поле той же записи. По всей видимости, InterBase обновляет записи в следующем порядке:

  1. Запись читается в память
  2. Вызываются триггеры before update в заданном порядке
  3. Проверяются все ограничения, наложенные на запись (а не только те, которые касаются обновляемых полей)
  4. Запись сохраняется
  5. Вызываются триггеры after update.
Таким образом, если прочитается null и триггеры это поле не исправят, то этап проверки гарантированно выругается.

Ещё более прикольно такие поля ведут себя при резервном копировании. Операция создания резервной копии проходит абсолютно безболезненно, без каких-либо замечаний. А вот при восстановлении процесс доходит до кривых полей и вылетает с руганью. Отсюда мораль: лучше всего резервные копии проверять "не отходя от кассы", о чём писалось выше.

Ещё один источник глюков - таблицы метаданных. Хотя они доступны для обновления только администратору, но ведь всё-таки доступны. Так что учитывая, что не все операции над структурой базы реализуемы операторами (якобы) SQL92, желание что-то исправить напрямую иногда возникает. Формально на этих таблицах висят какие-то триггера, которые иногда ругаются при попытках что-либо не совсем корректно обновить. Однако если руками залезть в метаданные, то ничего такого уже не гарантируется.

К примеру, можно создать генератор, потом процедуру, его использующую, а потом генератор из метаданных грохнуть. И грохнется, как миленький. А процедура будет спокойно работать. И можно вновь создать такой генератор с тем же именем. А процедура по-прежнему будет использовать старый счётчик, выживший где-то в дебрях системы. И все попытки сделать set generator будут действовать именно на новую версию. Вот если сделать alter procedure или drop/create, тогда начнёт использоваться новый генератор.

И это далеко не единственный пример нарушения метаданных, просто я его лучше всех запомнил.

Разумеется, мощным оружием в борьбе с физической целостностью являются внешние функции, о которых уже писалось в разделе, посвящённом "падучести" сервера. Когда такая функция разрушает серверный процесс, он в лучшем случае может прервать обновление физических структур базы в совершенно произвольном месте (да здравствует многопоточность!), а в худшем - пойдёт писать в БД данные, предварительно запоротые в памяти или в неправильные страницы. Для описания здесь возможных последствий мне потребовалась бы непереводимая игра слов с использованием местных идиоматических выражений. В общем, регулярно проверяйте базу!

Ещё пример: создаём процедуру с определёнными параметрами, затем вызываем её из другого места и в конце делаем alter procedure с совершенно другими параметрами. Ясно, что старый вызов после этого станет некорректным, но InterBase на это ничего не скажет. А что произойдёт при вызове в устаревшем формате ... как меня это достало! Не пытайтесь вызывать процедуры с ткамими ссылками, если Вам о них известно. Лучший вариант - грохнуть всё, что связано с ситуацией и пересоздать заново.

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

 
« Предыдущая статья