22. Пакеты (PACKAGE)

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

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

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

Модульность

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

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

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

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

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

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

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

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

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

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

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

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

Листинг 22.1 Синтаксис оператора создания заголовка пакета CREATE PACKAGE

CREATE PACKAGE <имя пакета>
[SQL SECURITY {DEFINER | INVOKER}]
AS
BEGIN
   [ <объявление процедуры> | <объявление функции> ...]
END

<объявление процедуры> ::=
   PROCEDURE <имя процедуры> [(<входной параметр> [, <входной параметр> ...])]
      [RETURNS (<выходной параметр> [, <выходной параметр> ...])]

<объявление функции> ::=
   FUNCTION <имя функции> [(<входной параметр> [, <входной параметр> ...])]
      RETURNS <тип> [COLLATE <сортировка>] [DETERMINISTIC]

<входной параметр> ::= <описание параметра> [{=|DEFAULT} <значение по умолчанию>]

<выходной параметр> ::= <описание параметра>

<описание параметра> ::= <имя параметра> <тип> [NOT NULL]
                         [COLLATE <порядок сортировки>]

<тип> ::= <тип данных SQL>
        | [TYPE OF] <имя домена>
        | TYPE OF COLUMN <имя таблицы/представления>.<имя столбца>

<значение по умолчанию>::= {<литерал> | NULL | <контекстная переменная>}

Примечание

Создать новый заголовок пакета может только администратор и пользователь с привилегией CREATE PACKAGE.

Пользователь, создавший заголовок пакета становится владельцем пакета.

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

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

Подробное описание заголовков хранимых процедур и функций можно найти в соответствующих разделах (см. раздел 19.1 и раздел 20.1).

Привилегии выполнения

Необязательное предложение SQL SECURITY {DEFINER | INVOKER} определяет, в контексте какого пользователя будет выполняться пакет. Такое поведение действует на пакет в целом и действительны для всех подпрограмм пакета. Ключевое слово INVOKER (значение по умолчанию) указывает, что пакет выполняется с правами вызвавшего его пользователя. Задание ключевого слова DEFINER означает, что пакет выполняется с правами к объектам базы данных его владельца (создателя). Значение по умолчанию на уровне всей базы данных можно изменить оператором ALTER DATABASE SET DEFAULT SQL SECURITY. Для процедур и функций, определенных в пакете, запрещено явно задавать предложение SQL SECURITY.

Входные параметры

Входные параметры заключаются в скобки после имени хранимой процедуры или функции. Параметры передаются по значению, то есть любые изменения значений входных параметров никак не влияют на значения этих параметров в вызвавшей программе. Входным параметрам может присваиваться значение по умолчанию. Параметры, для которых заданы значения по умолчанию, должны располагаться в самом конце списка. Если входной параметр основан на домене, которому также задано значение по умолчанию в предложении DEFAULT, то новое значение по умолчанию перекрывает указанное при описании домена. Для параметра можно указать ограничение NOT NULL, тем самым запретив передавать в него значение NULL. Для параметра строкового типа существует возможность задать порядок сортировки с помощью предложения COLLATE.

Выходные параметры

Для хранимых процедур список выходных параметров задаётся в необязательное предложение RETURNS.

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

Использование доменов при объявлении параметров

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

Использование типа столбца при объявлении параметров

Входные и выходные параметры, а также локальные переменные можно объявлять, используя тип данных столбцов существующих таблиц и представлений. Для этого используется предложение TYPE OF COLUMN, после которого указывается имя таблицы или представления и через точку имя столбца. При использовании TYPE OF COLUMN наследуется только тип данных, а в случае строковых типов ещё и набор символов, и порядок сортировки. Ограничения и значения по умолчанию столбца никогда не используются.

Детерминированные функции

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

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

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

Листинг 22.2 Синтаксис оператора изменения заголовка пакета ALTER PACKAGE

ALTER PACKAGE <имя пакета>
[SQL SECURITY {DEFINER | INVOKER}]
AS
BEGIN
   [ <объявление процедуры> | <объявление функции> ...]
END

<объявление процедуры> ::=
   PROCEDURE <имя процедуры> [(<входной параметр> [, <входной параметр> ...])]
      [RETURNS (<выходной параметр> [, <выходной параметр> ...])]

<объявление функции> ::=
   FUNCTION <имя функции> [(<входной параметр> [, <входной параметр> ...])]
      RETURNS <тип> [COLLATE <сортировка>] [DETERMINISTIC]

Примечание

Изменить заголовок пакета может администратор, владелец пакета или пользователь с привилегией ALTER ANY PACKAGE.

Позволяется изменять количество и состав процедур и функций, их входных и выходных параметров. Также данным оператором можно поменять в контексте какого пользователя будет выполняться пакет. При этом исходный код тела пакета сохраняется. Состояние соответствия тела пакета его заголовку отображается в таблице RDB$PACKAGES в столбце RDB$VALID_BODY_FLAG.

Изменить значение SQL SECURITY можно без указания тела пакета:

ALTER PACKAGE <имя пакета>
         SQL SECURITY {DEFINER | INVOKER}
       | DROP SQL SECURITY

22.3. Создание нового или изменение существующего заголовка пакета

Оператор CREATE OR ALTER PACKAGE создаёт новый или изменяет существующий заголовок пакета. Синтаксис оператора представлен в листинге .

Листинг 22.3 Синтаксис оператора CREATE OR ALTER PACKAGE

CREATE OR ALTER PACKAGE <имя пакета>
[SQL SECURITY {DEFINER | INVOKER}]
AS
BEGIN
   [ <объявление процедуры> | <объявление функции> ...]
END

<объявление процедуры> ::=
   PROCEDURE <имя процедуры> [(<входной параметр> [, <входной параметр> ...])]
      [RETURNS (<выходной параметр> [, <выходной параметр> ...])]

<объявление функции> ::=
   FUNCTION <имя функции> [(<входной параметр> [, <входной параметр> ...])]
      RETURNS <тип> [COLLATE <сортировка>] [DETERMINISTIC]

Если заголовок пакета не существует, то он будет создан с использованием предложения CREATE PACKAGE. Если он уже существует, то он будет изменен и перекомпилирован, при этом существующие привилегии и зависимости сохраняются.

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

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

Листинг 22.4 Синтаксис оператора удаления заголовка пакета DROP PACKAGE

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

Примечание

Выполнить удаление заголовка пакета может администратор, владелец пакета или пользователь с привилегией DROP ANY PACKAGE.

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

22.5. Создание нового или пересоздание существующего заголовка объекта

Оператор RECREATE PACKAGE создаёт новый или пересоздает существующий заголовок пакета. Синтаксис оператора представлен в листинге .

Листинг 22.5 Синтаксис оператора RECREATE PACKAGE

RECREATE PACKAGE <имя пакета>
[SQL SECURITY {DEFINER | INVOKER}]
AS
BEGIN
   [ <объявление процедуры> | <объявление функции> ...]
END

<объявление процедуры> ::=
   PROCEDURE <имя процедуры> [(<входной параметр> [, <входной параметр> ...])]
      [RETURNS (<выходной параметр> [, <выходной параметр> ...])]

<объявление функции> ::=
   FUNCTION <имя функции> [(<входной параметр> [, <входной параметр> ...])]
      RETURNS <тип> [COLLATE <сортировка>] [DETERMINISTIC]

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

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

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

Листинг 22.6 Синтаксис оператора создания тела пакета CREATE PACKAGE BODY

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

<объявление процедуры> ::=
   PROCEDURE <имя процедуры> [(<входной параметр> [, <входной параметр> ...])]
      [RETURNS (<выходной параметр> [, <выходной параметр> ...])]

<объявление функции> ::=
   FUNCTION <имя функции> [(<входной параметр> [, <входной параметр> ...])]
      RETURNS <тип> [COLLATE <сортировка>] [DETERMINISTIC]

<реализация процедуры> ::=
   PROCEDURE <имя процедуры> [(<входной_параметр> [, <входной_параметр> ...])]
      [RETURNS (<выходной параметр> [, <выходной параметр> ...])]
      {EXTERNAL NAME '<внешний модуль>' ENGINE <имя движка> [AS <тело вн. проц.>]} |
      { AS
         [<объявление> [<объявление> ...] ]
      BEGIN
         <блок операторов>
      END }

<реализация функции> ::=
   FUNCTION <имя функции> [(<входной_параметр> [, <входной_параметр> ...])]
      RETURNS <тип> [COLLATE <сортировка>] [DETERMINISTIC]
      {EXTERNAL NAME '<внешний модуль>' ENGINE <имя движка> [AS <тело вн. функ.>]} |
      { AS
         [<объявление> [<объявление> ...] ]
      BEGIN
         <блок операторов>
      END }

<входной параметр> ::= <описание параметра> [{=|DEFAULT} <значение по умолчанию>]

<выходной параметр> ::= <описание параметра>

<описание параметра> ::= <имя параметра> <тип> [NOT NULL]
                         [COLLATE <порядок сортировки>]

<тип> ::= {
   <тип данных SQL>
 | [TYPE OF] <имя домена>
 | TYPE OF COLUMN <имя таблицы/представления>.<имя столбца> }

<значение по умолчанию>::= {<литерал> | NULL | <контекстная переменная>}

<внешний модуль> ::= '<имя внешнего модуля>!<имя функции в модуле>[!<информация>]'

<объявление> ::= <объявление локальной переменной>;
               | <объявление курсора>;
               | <объявление процедуры/функции>
               | <реализация процедуры/функции>

Примечание

Выполнить оператор создания тела пакета может администратор, владелец пакета или пользователь с привилегией ALTER ANY PACKAGE.

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

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

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

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

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

Листинг 22.7 Синтаксис оператора удаления тела пакета DROP PACKAGE BODY

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

Примечание

Выполнить удаление заголовка пакета может администратор, владелец пакета или пользователь с привилегией DROP ANY PACKAGE.

22.8. Создание нового или пересоздание существующего тела объекта

Оператор RECREATE PACKAGE BODY создаёт новое или пересоздает существующее тело пакета. Синтаксис оператора представлен в листинге .

Листинг 22.8 Синтаксис оператора RECREATE PACKAGE BODY

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

<объявление процедуры> ::=
   PROCEDURE <имя процедуры> [(<входной параметр> [, <входной параметр> ...])]
      [RETURNS (<выходной параметр> [, <выходной параметр> ...])]

<объявление функции> ::=
   FUNCTION <имя функции> [(<входной параметр> [, <входной параметр> ...])]
      RETURNS <тип> [COLLATE <сортировка>] [DETERMINISTIC]

<реализация процедуры> ::=
   PROCEDURE <имя процедуры> [(<входной_параметр> [, <входной_параметр> ...])]
      [RETURNS (<выходной параметр> [, <выходной параметр> ...])]
      {EXTERNAL NAME '<внешний модуль>' ENGINE <имя движка> [AS <тело вн. проц.>]} |
      { AS
         [<объявление> [<объявление> ...] ]
      BEGIN
         <блок операторов>
      END }

<реализация функции> ::=
   FUNCTION <имя функции> [(<входной_параметр> [, <входной_параметр> ...])]
      RETURNS <тип> [COLLATE <сортировка>] [DETERMINISTIC]
      {EXTERNAL NAME '<внешний модуль>' ENGINE <имя движка> [AS <тело вн. функ.>]} |
      { AS
         [<объявление> [<объявление> ...] ]
      BEGIN
         <блок операторов>
      END }

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