Пакеты

Пакет — группа процедур и функций, которая представляет собой единый объект базы данных.

Пакеты состоят из двух частей: заголовка (ключевое слово PACKAGE) и тела (ключевые слова PACKAGE BODY). Сначала создаётся заголовок, а затем — тело.

Пакеты обладают следующими преимуществами:

Модульность

Блоки взаимозависимого кода выделены в логические модули, как это сделано в других языках программирования. В программировании существует множество способов для группировки кода, например с помощью пространств имен (namespaces), модулей (units) и классов. Со стандартными процедурами и функциями базы данных это невозможно.

Упрощение отслеживания зависимостей

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

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

Упрощение управления разрешениями

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

У упакованных подпрограмм нет отдельных привилегий. Привилегии действуют на пакет в целом. Привилегии, предоставленные пакетам, действительны для всех подпрограмм тела пакета, в том числе частных, и сохраняются для заголовка пакета.

Частные области видимости

Некоторые процедуры и функции могут быть частными (private), а именно их использование разрешено только внутри определения пакета.

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

Создание заголовка пакета

Оператор CREATE PACKAGE создаёт новый заголовок пакета.

CREATE PACKAGE <имя пакета>
AS
BEGIN
   [ <объявление константы> ]
   [ <объявление процедуры>]
   [ <объявление функции> ]
   [...]
END

Процедуры, функции и константы, объявленные в заголовке пакета, доступны вне тела пакета через полный идентификатор имён процедур, функций и констант (<имя пакета>.<имя процедуры> и <имя пакета>.<имя функции>). Процедуры, функции и константы, определенные в теле пакета, но не объявленные в заголовке пакета, не видны вне тела пакета.

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

Имя пакета должно быть уникальным среди имён всех пакетов. Имена процедур и функций, объявленные в заголовке пакета, должны быть уникальны среди имён процедур и функций, объявленных в заголовке и теле пакета.

Изменение заголовка пакета

Оператор ALTER PACKAGE изменяет заголовок пакета.

ALTER PACKAGE <имя пакета>
AS
BEGIN
   [ <объявление константы> ]
   [ <объявление процедуры>]
   [ <объявление функции> ]
   [...]
END

Удаление заголовка пакета

Оператор DROP PACKAGE удаляет существующий заголовок пакета.

DROP PACKAGE <имя пакета>

Перед удалением заголовка пакета, необходимо выполнить удаление тела пакета (DROP PACKAGE BODY), иначе будет выдана ошибка. Если от заголовка пакета существуют зависимости, то при попытке удаления такого заголовка будет выдана соответствующая ошибка.

Создание тела пакета

Оператор CREATE PACKAGE BODY создаёт новое тело пакета. Тело пакета может быть создано только после того как будет создан заголовок пакета. Если заголовка пакета с именем <имя пакета> не существует, то будет выдана соответствующая ошибка.

CREATE PACKAGE BODY <имя пакета>
AS
BEGIN
   [ <объявление константы> ]
   [ <объявление процедуры>]
   [ <объявление функции> ]
   [ <реализация процедуры>]
   [ <реализация функции> ]
   [...]
END

Все процедуры и функции, объявленные в заголовке пакета, должны быть реализованы в теле пакета с той же сигнатурой. Кроме того, должны быть реализованы и все процедуры и функции, объявленные в теле пакета, с той же сигнатурой. Процедуры и функции, определенные в теле пакета, но не объявленные в заголовке пакета, не видны вне тела пакета.

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

Удаление тела пакета

Оператор DROP PACKAGE BODY удаляет существующее тело пакета.

DROP PACKAGE BODY <имя пакета>

Пример пакета

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

CREATE PACKAGE APP_VAR
AS
BEGIN
  FUNCTION GET_DATEBEGIN() RETURNS DATE DETERMINISTIC;
  FUNCTION GET_DATEEND() RETURNS DATE DETERMINISTIC;
  PROCEDURE SET_DATERANGE(ADATEBEGIN DATE, ADATEEND DATE DEFAULT CURRENT_DATE);
END

Таким образом, пакет APP_VAR представляет собой некий интерфейс, который можно использовать в других модулях PSQL.

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

CREATE PACKAGE BODY APP_VAR
AS
BEGIN

  -- Возвращает дату начала периода
  FUNCTION GET_DATEBEGIN() RETURNS DATE DETERMINISTIC
  AS
  BEGIN
    RETURN RDB$GET_CONTEXT('USER_SESSION', 'DATEBEGIN');
  END

  -- Возвращает дату конца периода
  FUNCTION GET_DATEEND() RETURNS DATE DETERMINISTIC
  AS
  BEGIN
    RETURN RDB$GET_CONTEXT('USER_SESSION', 'DATEEND');
  END

  -- Устанавливает период
  PROCEDURE SET_DATERANGE(ADATEBEGIN DATE, ADATEEND DATE)
  AS
  BEGIN
    RDB$SET_CONTEXT('USER_SESSION', 'DATEBEGIN', ADATEBEGIN);
    RDB$SET_CONTEXT('USER_SESSION', 'DATEEND', ADATEEND);
  END
END

Для хранения установленных значений применяются функции для работы с контекстными переменными RDB$SET_CONTEXT и RDB$GET_CONTEXT.

Контрольные вопросы

  1. Что такое пакет и из чего он состоит?
  2. Назовите преимущества использования пакетов?
  3. Можно ли объявлять хранимые процедуры и функции только в заголовке пакета?
  4. Можно ли объявлять хранимые процедуры и функции только в теле пакета?