Отслеживание зависимостей

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

Чтобы обеспечить целостность структуры всей базы данных, QHB делает так, чтобы вы не могли удалить объекты, от которых все еще зависят другие объекты. Например, попытка удалить таблицу товаров, которая рассматривалась в подразделе Ограничения внешнего ключа, с зависящей от нее таблицей заказов, приведет к появлению сообщения об ошибке приблизительно такого содержания:

DROP TABLE products;

ERROR:  cannot drop table products because other objects depend on it
DETAIL:  constraint orders_product_no_fkey on table orders depends on table products
HINT:  Use DROP ... CASCADE to drop the dependent objects too.
-- ОШИБКА: удалить таблицу products невозможно, потому что от нее зависят другие объекты
-- ДЕТАЛИЗАЦИЯ: ограничение orders_product_no_fkey для таблицы orders зависит от таблицы products
-- СОВЕТ: Для удаления зависимых объектов используйте DROP ... CASCADE

Сообщение об ошибке содержит полезный совет: если вы не хотите удалять все зависимые объекты по отдельности, можно выполнить:

DROP TABLE products CASCADE;

и все зависимые объекты будут удалены, равно как и любые объекты, которые рекурсивно зависят от них. В данном случае таблица заказов не удаляется, а удаляется только ограничение внешнего ключа. На этом все заканчивается, потому что от ограничения внешнего ключа ничего не зависит. (Если вы хотите проверить, что будет делать DROP ... CASCADE, запустите DROP без CASCADE и прочитайте вывод DETAIL.)

В QHB почти все команды DROP поддерживают указание CASCADE. Конечно, характер возможных зависимостей зависит от типа объекта. Кроме того, вместо CASCADE можно написать RESTRICT, чтобы получить поведение по умолчанию, которое должно предотвратить удаление объектов, от которых зависят любые другие объекты.

Примечание
Согласно стандарту SQL, в команде DROP требуется явно указывать либо RESTRICT, либо CASCADE. На самом деле ни в одной СУБД это правило не действует, но будет ли поведением по умолчанию RESTRICT или CASCADE, зависит от конкретной СУБД.

Если в команде DROP перечисляется несколько объектов, CASCADE требуется только при наличии зависимостей вне указанной группы. Например, в команде, скажем, DROP TABLE tab1, tab2 наличие внешнего ключа, ссылающегося на tab1 из tab2, не будет означать, что для успешного выполнения команды необходимо CASCADE.

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

CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow',
                             'green', 'blue', 'purple');

CREATE TABLE my_colors (color rainbow, note text);

CREATE FUNCTION get_color_note (rainbow) RETURNS text AS
  'SELECT note FROM my_colors WHERE color = $1'
  LANGUAGE SQL;

(Функции языка SQL рассматриваются в разделе Функции на языке запросов (SQL).) QHB будет знать, что функция get_color_note зависит от типа rainbow: удаление типа приведет к принудительному удалению функции, поскольку тип ее аргумента больше не определяется. Но QHB не будет считать, что get_color_note зависит от таблицы my_colors, и поэтому не будет удалять эту функцию при удалении таблицы. Хотя у этого подхода есть недостатки, но есть и преимущества. При отсутствии таблицы функция в некотором смысле все еще будет действующей, хотя ее выполнение вызовет ошибку; создание новой таблицы с тем же именем позволит функции снова заработать.

С другой стороны, у функции или процедуры на языке SQL, тело которой написано в стиле стандарта SQL, это тело анализируется при ее определении, и все зависимости, распознаваемые синтаксическим анализатором, сохраняются Таким образом, если мы запишем функцию из примера выше как

CREATE FUNCTION get_color_note (rainbow) RETURNS text
BEGIN ATOMIC
  SELECT note FROM my_colors WHERE color = $1;
END;

то зависимость функции от таблицы my_colors будет известна, и к ней будет применена команда DROP.