Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lektsii.doc
Скачиваний:
39
Добавлен:
19.05.2015
Размер:
2.59 Mб
Скачать

IV Процедурный язык pl/sql

ДЕ14: Блоки PL/SQL

Язык PL/SQL является составной частью во многих продуктах Oracle. Сервер Oracle включает поддержку языка PL/SQL, предоставляя пользователю возможность создавать и использовать на сервере процедуры и триггеры базы данных, выполняющие задачи конкретного приложения. Моделью PL/SQL служил язык программирования Ада, поэтому PL/SQL обладает набором средств, характерных для любого современного языка программирования. Программы, создан­ные на языке PL/SQL, могут работать совместно в различ­ных частях прикладной системы, построенной на средствах Oracle. Например, в приложении, использующем работу с формами, триггер может вызывать для выполнения некоторо­го действия хранимую процедуру.

Знакомство с PL/SQL необходимо каждому разработ­чику приложений для Oracle. С помощью PL/SQL можно улучшить производительность разрабатываемого приложения и системы в целом. Вместо интерпретируемых операторов SQL система Oracle позволяет использовать предварительно скомпилированные и, следовательно, быстро выполняющиеся процедуры, созданные на языке PL/SQL. Дополнительным преимуществом является значительное сокращение сетевого графика между приложением и сервером.

Всякая программа на PL/SQL состоит из трех блоков:

блоки описаний, исполнительного блока и блока обработки исключительных ситуации. Исполнительный блок может быть структурирован с использованием операторных скобок BEGIN и END.

Синтаксически программа на PL/SQL оформляется следующим образом:

DECLARE

операторы…

BEGIN

операторы…

EXCEPTION

операторы…

END;

В блоке DECLARE описываются переменные, констан­ты и определяемые пользователем типы данных. Первый оператор BEGIN отмечает начало тела основной программы. В тело программы могут быть вложены другие блоки, огра­ниченные операторными скобками BEGIN и END. В блоке EXCEPTION определяются фрагменты программного кода для обработки исключительных ситуации в программе. По­следний оператор END указывает конец тела программы.

В любые части программы на PL/SQL можно включать комментарии. Текст, который начинается с символов «—« и продолжается до конца текущей строки, рассматривается как комментарий. Строка, начинающаяся с ключевого слова REM, также рассматривается как комментарий. Использование комментариев является хорошей практикой составления программ. Перед блоком DECLARE могут присутствовать команды установки переменных окружения для PL/SQL.

В блоке программы PL/SQL, ограниченном операто­рами DECLARE и BEGIN, описываются переменные и кон­станты. Любая переменная или константа должна иметь один из допустимых в Oracle типов. Константа идентифицируется ключевым словом CONSTANT и отличается от переменной тем, что попытка изменить ее значение приводит к сообще­нию об ошибке. Присваивание значений переменным осуще­ствляется оператором «: =».

PL/SQL, как и любой другой процедурный язык программирования, состоит из логически связанных элементов, объединенных в программные единицы, которые называются блоками. Каждый модуль PL/SQL состоит как минимум из одного блока. Блоки PL/SQL могут содержать любое количество подблоков, то есть иметь различный уровень вложенности. Блок как структурная единица логически связанных элементов определяет область их действия, делает код читабельным и простым для понимания. В PL/SQL различают два типа блока:

анонимные блоки;

именованные блоки.

Анонимные блоки

Анонимные блоки - это блоки, которые не имеют имени. Анонимные блоки не могут быть вызваны другими блоками, так как у них нет имени, на которое можно ссылаться.

Триггеры Oracle Forms и Reports, которые также называются клиентскими триггерами, являются анонимными блоками PL/SQL. Триггеры базы данных и сценарии в SQL*Plus, заключенные в операторские скобки BEGIN и END, также являются анонимными блоками. Ниже приведена структура анонимного блока:

DECLARE

<имя переменной > <тип данных><(значение)>;

BEGIN

< исполняемый оператор>;

EXCEPTION

< оператор обработки исключения >;

END;

DECLARE - раздел объявлений. В этом разделе идентифицируются переменные, курсоры и вложенные блоки, на которые имеются ссылки в исполняемом блоке и блоке исключений. Этот раздел необязательный.

BEGIN-END - исполняемый раздел. В этом разделе находятся операторы, которые выполняются ядром PL/SQL во время работы вашего приложения. Этот раздел является обязательным.

EXCEPTION - раздел исключений. В этом разделе обрабатываются различные исключения, предупреждения и ошибки. Этот раздел необязателен.

Из всех ключевых слов в представленной структуре для анонимного блока ключевые слова BEGIN и END являются обязательными, и между ними должен быть как минимум один исполняемый оператор:

BEGIN

Null;

END;

Несмотря на то что ключевые слова BEGIN и END обязательны, в Oracle Forms их можно опустить. Так, к примеру, простейший анонимный блок имеет вид:

BEGIN

Message ('Hello!');

END;

В Oracle Forms этот анонимный блок можно записать и без ключевых слов BEGIN и END:

Message ('Hello!');

Рассмотрим различные виды анонимных блоков.

Блок с разделом объявлений и исключений:

DECLARE

<имя переменной > <тип данных><(значение)>;

BEGIN

< исполняемый оператор>;

EXCEPTION

< оператор обработки исключения >;

END;

Пример:

BEGIN

NULL;

EXCEPTION

WHEN OTHERS THEN NULL;

END;

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

Синтаксис:

BEGIN

< исполняемый оператор >;

BEGIN

< исполняемый оператор >;

EXCEPTION

< оператор обработки исключения >;

END;

EXCEPTION

< оператор обработки исключения >;

END;

Практический пример:

BEGIN

NULL;

BEGIN

NULL;

EXCEPTION

WHEN OTHERS THEN NULL;

END;

NULL;

EXCEPTION

WHEN OTHERS THEN NULL;

END;

Вложенный анонимный блок с разделом объявлений и исключений:

Синтаксис:

DECLARE

<имя переменной > <тип данных><(значение)>;

BEGIN

< исполняемый оператор >;

BEGIN

< исполняемый оператор >;

EXCEPTION

< оператор обработки исключения >;

END;

EXCEPTION

< оператор обработки исключения >;

END;

Практический пример:

DECLARE

x NUMBER(4);

BEGIN

x := 1000;

BEGIN

x := x + 100;

EXCEPTION

WHEN OTHERS THEN x := x + 2;

END;

x := x + 10;

DBMS_OUTPUT.PUT_LINE(x);

EXCEPTION

WHEN OTHERS THEN

x := x + 3;

END;

Когда вы связываете код PL/SQL с триггером или полем, пользуясь таким инструментальным средством, как Forms Builder, этот код составляет анонимный блок PL/SQL. При этом можно создать полный блок с объявлениями, исполняемыми операторами и разделом обработки исключений или же ограничиться только исполняемыми операторами.

Именованные блоки

Именованные блоки - это блоки, которые имеют имя, например, функция или процедура. Несмотря на то что анонимные блоки используются часто, каждый разработчик пытается оформить свою PL/SQL-программу как именованный блок. Преимущество именованного блока в том, что у него есть имя и на него можно ссылаться из других блоков. Если считать главным отличием между анонимным блоком и именованным отсутствие имени у первого, то тогда в Forms понятие анонимного блока очень размыто. В Oracle Forms, несмотря на то что пользовательский триггер будет считаться анонимным блоком, его все же можно вызвать и на него можно ссылаться. Ниже приведен пример именованного блока функции и процедуры:

PROCEDURE [схема.] имя [(параметр [, параметр ...])]

[AUID {DEFINER | CURRENT_USER}]

FUNCTION [схема.] имя [(параметр [, параметр ...])]

RETURN тип_возвращаемых_данных

[AUID {DEFINER | CURRENT_USER}]

[DETERMINISTIC]

[PARALLEL ENABLE ...]

[PIPELINED]

Заголовок функции отличается от заголовка процедуры лишь ключевым словом RETURN.

Именованные блоки, так же как и анонимные, могут быть вложенными, причем анонимный блок может быть вложен в именованный блок. Ниже приведен пример такого блока:

PROCEDURE calc_totals

IS

year_total NUMBER;

BEGIN

year_total := 0;

/* Начало вложенного блока */

DECLARE

month_total NUMBER;

BEGIN

month_total := year_total / 12;

END set_month_total;

/* Конец вложенного блока */

END;

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

Управление выполнением программы

Операторы большинства языков программирования, в том числе и языка PL/SQL, выполняются последовательно. Такая схема называется потоком команд. В любом развитом языке присутствует более или менее богатый набор операто­ров управления потоком команд. Соответствующие операто­ры позволяют выполнять условные переходы, циклически выполнять группу операторов и осуществлять выход из цикла при выполнении определенных условии. В PL/SQL преду­смотрено несколько операторов, с помощью которых можно управлять выполнением программы. Рассмотрим соответст­вующие программные конструкции.

Оператор ветвления

Оператор IF. . .THEN. . .ELSE позволяет проверить условие и, в зависимости от результатов проверки (TRUE или FALSE), выполнить различные группы операторов. Альтер­нативная последовательность операторов определяется клю­чевым словом ELSE. Границы действия оператора IF опре­деляются закрывающей операторной скобкой END IF. Для расширения структуры ветвления дополнительно предусмот­рены операторные скобки ELSIF, задающие структуры ветв­ления более глубокого уровня.

Oracle использует следующий синтаксис конструкции ветвления в PL/SQL:

IF условие _1 THEN операторы_1; — ветвь!

ELSIF условие_2 THEN операторы_2; - ветвь2

ELSIF ...

ELSE операторы_п; — операторы альтернативы END IF;

Обратите внимание, что оператор дополнительного ветвления кодируется ключевым словом ELSIF (а не ELSEIF, как иногда предполагают не очень внимательные пользователи).

Операторы цикла

Организация цикла оформляется в программе на PL/SQL оператором LOOP. Выйти из цикла можно несколь­кими способами. Конструкция EXIT WHEN обеспечивает выход из цикла при выполнении условия в соответствующем операторе.

Еще одним распространенным вариантом организации цикла является цикл, управляемый ключевым словом WHILE. Управление типа WHILE обеспечивает выполнение цикла до тех пор, пока условие, определенное ключевым словом WHILE, является истинным (TRUE).

Цикл, управляемый оператором FOR, используется в том случае, когда точно известно, сколько раз нужно выпол­нять итерацию цикла. Рассмотрим пример расчета факториа­ла заданного числа. Обратите внимание, что переменную цикла (в данном случае I) описывать в блоке DECLARE не нужно.

Обработка исключительных ситуации

Большинство развитых языков программирования об­ладают встроенными механизмами обработки исключитель­ных ситуации. Соответствующие языковые средства преду­смотрены и в PL/SQL. При возникновении в системе предо­пределенной или описанной пользователем ситуации проис­ходит автоматическая передача управления в нужный фраг­мент блока EXCEPTION программы на PL/SQL, где и про­исходит предусмотренная обработка возникшей исключи­тельной ситуации.

Некоторые предопределенные исключительные ситуа­ции PL/SQL представлены в таблице 4. Полный перечень исключительных ситуаций может быть найден в руководстве но языку PL/SQL.

Символическое имя предопределенной исключительной ситуации

Описание предопределенной исключительной ситуации

LOGINDENIED

Неуспешное подключение к сер­веру (например, введен ошибоч­ный пароль).

NOTLOGGEDON

Попытка выполнить действие без подключения к серверу Oracle.

INVALIDCURSOR

Ссылка на недопустимый курсор или недопустимая операция с кур­сором.

NODATAFOUND

Не найдены данные, соответству­ющие оператору SELECT INTO.

DUPVALONINDEX

Попытка вставить значение-дубликат в столбец с ограничени­ем на уникальность значения.

TOOMANYROWS

Оператор SELECT INTO возвра­щает более одной строки в пере­менную.

VALUEERROR

Арифметическая ошибка, ошибка преобразования или усечения.

Рассмотрим пример программы с обработкой исклю­чительных ситуаций. В тексте программы пропущен опера­тор открытия курсора, поэтому при обращении к методу % FOUND неоткрытого курсора возникнет исключительная ситуация INVALID_CURSOR.

Для обработки исключительных ситуаций, не входящих в перечень стандартных, можно использовать специальный обработчик PL/SQL OTHERS или описать пользовательскую исключительную ситуацию и запрограммировать ее обработ­ку. Ключевое слово OTHERS блока EXCEPTION определяет механизм универсальной обработки исключительных ситуа­ций, не вошедших в список ситуаций, обрабатываемых явно в блоке EXCEPTION. Использование специального обработ­чика исключительных ситуаций OTHERS является хорошим стилем программирования, при котором в программе не воз­никает необработанных, а следовательно, и незамеченных, исключительных ситуаций.

Рассмотрим технику определения и обработки ис­ключительных ситуации, задаваемых пользователем. Исклю­чительная ситуация, определяемая пользователем, должна быть описана в блоке DECLARE программы. В PL/SQL ис­пользуется следующий синтаксис описания исключительной ситуации:

Имя_исключительной_ситуации EXCEPTION;

В программе условие возникновения исключительной ситуации определяется стандартными средствами PL/SQL, чаще всего операторами IF... THEN. После обнаружения условий возникновения исключительной ситуации она гене­рируется оператором RAISE. В PL/SQL используется сле­дующий синтаксис генерации исключительной ситуации:

RAISE Имя_исключительной_ситуации;

Оператор RAISE генерирует определенную пользова­телем исключительную ситуацию и передает управление на соответствующий обработчик исключительной ситуации, который определен в блоке EXCEPTION.

ДЕ15: Работа с данными в PL/SQL.

Замена одного типа другим

В строго типизованных языках иногда необходимо преобразовать один тип в другой. Это, как правило, требуется, когда присваивается значение переменной или вычисляется выраже­ние. Существует два вида преобразования типов — неявное и явное.

Неявное преобразование типов

Неявное преобразование выполняется компилятором языка PL/SQL без вмешательства со стороны разработчика, например, следующим образом:

DECLARE

flt_x FLOAT(3,2) := 2.25;

int_y INTEGER(6) := 100;

flt_z DOUBLE(10,2); BEGIN

fit z := flt_x + int_y; — неявное преобразование типов END; /

Для правильного выполнения арифметических действий осуществляется неявное преобра­зование целого значения в значение с плавающей точкой. Это типично для компиляторов. Значение преобразуется в более общий тип для выполнения вычислений. В языке PL/SQL тип VARCHAR2 является самым общим. В табл. представлены возможные виды неявного пре­об­разования типов. Если необходимого преобразования нет в таблице, его следует выполнить явно, используя встроенную функцию преобразования.

Тип PLS INTEGER имеет те же характеристики неявного преобразования, что и BINARY INTEGER, включая неявное преобразование друг в друга. Ниже приведен пример пра­вильных объявлений и присваиваний:

DECLARE datejrom DATE := '20-AUG-85';

  • char в date (совпадает с форматом NLS) cnt INTEGER(3) := '0';

  • char в integer (подтип типа number) loop_control BINARY_INTEGER;

bin_val RAW(-2) := 'Г; — char в raw

str VARCHAR2(9) := '26-AUG-60'; — char в varchar2 short_num VARCHAR2(3); BEGIN loop_control := cnt + 'Г;

— char в number и в binary integer str := date from; — date в varchar2

short_num := bin_val; — raw в varchar2

cnt := shortjium; — varchar2 в integer

Любые значения могут быть неявно преобразованы в типы и из типов CHAR и VARCHAR2. Если необходимо преобразовать значение RAW в NUMBER, можно сначала конвертировать зна­че­ние RAW в VARCHAR2, а затем — в NUMBER. Неявные преобразования действуют на подтипы этих типов в той же мере, что и на основные типы.

Для типа DATE неявное преобразование из CHAR или VARCHAR2 может быть успешным толь-, ко в том случае, если формат строки — NLS, по умолчанию определенный для этой базы данных. Если строка имеет другой формат, необходимо использовать явное преобразование и применить результат конвертирования.

Явное преобразование типов

В табл. перечислены все функции языка PL/SQL для явного преобразования типов данных.

Здесь

  • fmt — форматная строка;

  • lang — форматная строка NLS.

Функция ТО CHAR() возвращает VARCHAR2, но она может быть легко использована для CHAR, так как эти два типа преобразуются неявно. Отметим, однако, что не существует функ­ций преобразования для типа LONG. Для того чтобы сдать что-либо с LONG, необходимо снача­ла неявно преобразовать его в тип VARCHAR2, а затем явно — в какой-либо другой тип.

Возможные типы преобразований.

BINARY_INTEGER

CHAR

DATE

LONG

NUMBER

RAW

ROWID

VARCHAR2

BINARY_INTEGER

+

+

+

+

CHAR

+

+

+

+

+

+

+

DATE

+

+

+

LONG

+

+

+

NUMBER

+

+

+

+

RAW

+

+

+

ROWID

+

+

VARCHAR2

+

+

+

+

+

+

+

+

В PL/SQL VARCHAR2 – наиболее общий тип.

PLS_INTEGER имеет те же характеристики неявного

преобразования, что и BINARY_INTEGER

Определение композитных (агрегатных) типов данных.

2 типа Record и Table

Фактически использования базированной переменной %ROWTYPE это и есть определения типа Record. Для определения композитного типа используется конструкция типа TABLE OF.

Пример:

DECLARE

TYPE A IS RECORD OF ( b INTEGER:=0; b VARCHAR2(32767));

TYPE C IS TABLE OF CHAR(80) INDEX BY BINARY_INTEGER;

d A; e C;

Тип RECORD определяет структуру с любым числом переменных любого типа, включая ранее определенные типы Record и Table. Доступ к отдельным переменным так же как и при базировании %ROWTYPE(через точку). Переменные базирующие на типе ROWTYPE таблицы БД и переменные типа RECORD всегда не совместимы даже если их элементы совпадают.

Тип данных TABLE не отношение, а это практически не ограниченный массив, элементы которого не существуют, пока не получено определенное значение. Массивы индексируются типами BINARY_INTEGER (±2147483647). На элементы массива ссылаются путем указания индекса в ().

Пример:

DECLARE

TYPE STR_TYPE IS TABLE OF VARCHAR2(8) INDEX BY BINARY_INTEGER;

arr STR_TYPE;

i BINARY_INTEGER:=17;

BEGIN

arr(2147483647):=’big’;

arr(0):=’zero’;

arr(-2347483647):=’small’;

arr(i):=’abc’;

Замечание: элемент типа TABLE получает буфер, размер которого соответствует типу данных и объявленному размеру + небольшие накладные расходы. Если строка таблицы состоит из элементов VARCHAR2(32767), то каждая строка хранящаяся в таблицы требует размер буфера 32Кб, даже если в ней хранится 1 Кб.

При описании типа TABLE нужно указать, что хранящееся значение не NOT NULL.

Пример:

DECLARE

TYPE STR_TYPE IS TABLE OF VARCHAR2(8) NOT NULL

INDEX BY BINARY_INTEGER;

arr STR_TYPE;

i BINARY_INTEGER:=17;

BEGIN

arr(2147483647):=’big’;

arr(0):=’zero’;

arr(-2347483647):=’small’;

arr(i):=NULL;

Для описания типа строки можно использовать базированную ссылку.

DECLARE

TYPE A_TYPE IS TABLE OF B.A%TYPE NOT NULL INDEX BY BINARY_INTEGER;

C A_TYPE;

C(0):=0;

Плюсы использования массивов в PL/SQL

  1. Возможность передачи подпрограммы и из них больших объемов данных.

  2. Повышение скорости операций с большими объемами данных.

  3. Возможность загрузки из таблиц Oracle таблиц PL/SQL с целью повышения скорости манипулирования данными.

  4. Упрощение операции выборки информации из таблиц (массивов).

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

Пример:

DECLARE

TYPE ORD_TYPE IS TABLE OF a.item_no %TYPE INDEX BY BINARY_INTEGER;

clr_ord ORD_TYPE;

new_ord, old_ord ORD_TYPE;

BEGIN

old_ord:=new_od;

new_ord:=clr_ord;

END;

С версии 7.3 переменные таблицы могут базироваться на таблицах БД и объявленных типов RECORD, каждый элемент записи должен иметь скалярный тип.

Пример:

DECLARE

TYPE ORD_TYPE IS TABLE OF a%ROWTYPE INDEX BY BINARY_INTEGER;

q a.quan%TYPE;

ord_tab_rec ORD_TYPE;

BEGIN

q:=ord_tab_rec(i).quan;

Методы таблиц PL/SQL

Наименование

Возвращаемый тип

Способ использования

Существование элемента в таблице

True/false

<имя таблицы>.exit(<смещение>)

Число элементов в массиве

BINARY_INTEGER

<имя таблицы>.Count

Смещение 1-го элемента

BINARY_INTEGER

<имя таблицы>.First

Смещение последнего элемента

BINARY_INTEGER

<имя таблицы>.Last

Смещение предыдущего элемента

BINARY_INTEGER

<имя таблицы>.Prior

Смещение последующего элемента

BINARY_INTEGER

<имя таблицы>.Next

Удаление

Null

<имя таблицы>.Delete – удаление всей таблицы

<имя таблицы>.Delete(n) – удаление элемента со смещением n

<имя таблицы>.Delete(n,m) – удаляет элементы из диапазона [n,m]

Записи и Коллекции

Коллекцией называется упорядоченная группа элементов одного типа. Язык PL/SQL поддерживает три вида коллекций:

вложенные таблицы (nested tables);

индексированные таблицы;

varray-массивы (variable-size arrays).

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

для обмена с таблицами баз данных и столбцами данных;

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

Для создания коллекции следует определить тип коллекции - TABLE или VARRAY - и объявить переменную этого типа. Определение типа выполняется в секции объявлений блока PL/SQL, подпрограммы или пакета.

Вложенные таблицы

Определение типа вложенной таблицы может иметь следующее формальное описание:

TYPE type_name IS TABLE OF

element_type [NOT NULL];

Параметр type_name указывает имя определяемого типа, а element_type - это любой допустимый тип данных PL/SQL, исключая некоторые типы, в том числе VARRAY, TABLE, BOOLEAN, LONG, REF CURSOR и т.п.

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

Индексированные таблицы

Индексированные таблицы позволяют работать со столбцами как с единой переменной - массивом.

Определение индексированной таблицы (index-by tables) может иметь следующее формальное описание:

TYPE type_name IS TABLE

OF element_type [NOT NULL]

INDEX BY BINARY_INTEGER;

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

VARRAY-массивы

Определение типа Varray-массива может иметь следующее формальное описание:

TYPE type_name IS

{VARRAY | VARYING ARRAY} (size_limit)

OF element_type [NOT NULL];

Параметр type_name указывает имя определяемого типа, size_limit - максимальное количество элементов, а element_type - это любой допустимый тип данных PL/SQL, исключая некоторые типы, такие как VARRAY, TABLE, BOOLEAN, LONG, REF CURSOR и т.п.

Если типом элемента является тип "запись", то каждое поле записи должно быть скалярного или объектного типа.

Максимальное количество элементов в Varray-массиве указывается при определении типа и не может изменяться динамически. Доступ к каждому элементу Varray-массива осуществляется по индексу. Varray-массивы можно передавать в качестве параметров. Varray-массивы не могут иметь пустот, так как для них нет операции удаления произвольного элемента массива.

Например:

DECLARE

TYPE d1 IS VARRAY(365) OF DATE;

TYPE rec1 IS

RECORD (v1 VARCHAR2(10),

v2 VARCHAR2(10));

- Массив записей

TYPE arr_rec IS VARRAY(250) OF rec1;

- Вложенная таблица

TYPE F1T1 IS TABLE OF tbl1.f1%TYPE;

CURSOR c1 IS SELECT * FROM tbl1;

- Массив записей,

- основанный на курсоре

TYPE t1 IS VARRAY(50) OF c1%ROWTYPE;

TYPE t2 IS TABLE OF tbl1%ROWTYPE

- Индексированная таблица

INDEX BY BINARY_INTEGER;

- Объявление переменной

rec_t2 t2;

BEGIN

/* Использование переменной

типа "индексированная таблица" */

SELECT * INTO rec_t2(120)

WHERE f1 = 120;

END;

Инициализация коллекций

Для инициализации коллекции используется конструктор - автоматически создаваемая функция, одноименная с типом коллекции.

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

Например:

DECLARE

CREATE TYPE rec_var1

AS VARRAY(3) OF num;

CREATE TYPE rec_var2

AS VARRAY(3) OF rec_obj;

r1 rec_var1; r2 rec_var2;

BEGIN

/* Инициализация коллекции

из трех элементов */

r1 := rec_var1 (2.0, 2.1, 2.2);

/*Инициализация varray-массива,

содержащего объекты типа rec_obj */

r2 := rec_var2 (rec_obj(1, 100, 'fff'),

rec_obj (2,110, 'ggg'),

rec_obj (3,120, 'jjj'));

Оператор CREATE TYPE позволяет сохранить определяемый тип в базе данных.

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

Например:

BEGIN

INSERT INTO tbl_coll

VALUES (1, 'aaa', rec_obj

(3,120, 'jjj'));

Методы, применяемые при работе с коллекциями

В PL/SQL реализован ряд встроенных методов для работы с коллекциями. Эти методы вызываются как

collection_name.method_name[(parameters)]

Эти методы нельзя вызывать из SQL-оператора.

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

Метод Описание

EXISTS(n) Если n-ый элемент коллекции существует, то функция возвращает значение TRUE

COUNT Функция возвращает реальное количество элементов, которые содержит коллекция

LIMIT Функция возвращает размер varray-массива или NULL - для вложенных таблиц

DELETE(m,n) Эта процедура удаляет элементы из вложенной или индексированной таблицы. Если параметров не задано, то удаляются все элементы коллекции. При задании параметра n удаляется n-ый элемент вложенной таблицы, а если задано оба параметра, то удаляются все элементы в диапазоне от n до m

FIRST, LAST Функции возвращают наименьший и наибольший индекс элементов коллекции. Для пустой вложенной таблицы обе функции возвращают значение NULL. Для Varray-массивов вызов функции FIRST всегда возвращает значение 1

PRIOR(n) Эта функция используется для цикла или последовательного просмотра элементов вложенных таблиц и возвращает индекс элемента, предшествующего указанному параметром n. Если такого элемента нет, то возвращается значение NULL

NEXT(n) Функция употребляется для цикла или последовательного просмотра элементов вложенных таблиц и возвращает индекс элемента, следующего за указанным параметром n. Если такого элемента нет, то возвращается значение NULL

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

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

Применение функций TRIM и EXTEND реализует для вложенных таблиц механизм стека, позволяя удалять элементы и добавлять их в конец вложенной таблицы. Функция DELETE выполняет удаление элементов, оставляя пустые места, которые впоследствии учитываются функцией TRIM.

Значение, возвращаемое функцией COUNT, может использоваться как максимальное значение для счетчика цикла по элементам коллекции.

Например:

FOR i IN 1..tbl1.COUNT LOOP

END LOOP;

Функция COUNT также позволяет определить количество строк, которые были извлечены из столбца базы данных во вложенную таблицу.

Для Varray-массивов значение, возвращаемое функцией COUNT, эквивалентно значению, возвращаемому функцией LAST. Для вложенных таблиц эти значения могут быть различны в том случае, если выполнялась процедура DELETE, удаляющая элементы из коллекции.

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

Например:

DECLARE

- Вложенная таблица

TYPE cl IS TABLE OF VARCHAR2(10);

c1 cl;

BEGIN

- Инициализации

- коллекции конструктором

c1 := cl('с 1', 'с 2', 'с 3');

- Удаление последнего (3-го) элемента

c1.DELETE(c1.LAST);

- Удаление двух последних элементов:

- (2-го и 3-го)

c1.TRIM(c1.COUNT);

- Запись в поток

- вывода значения 'с 1'

DBMS_OUTPUT.PUT_LINE(c1(1));

END;

Если при работе с коллекцией происходит ошибка, то Oracle инициирует бросок исключения. В следующей таблице приведены основные причины возникновения ошибок для коллекций.

Исключение Причина ошибки

COLLECTION_IS_NULL Коллекция не была инициализирована

NO_DATA_FOUND Индекс ссылается на ранее удаленный элемент коллекции

SUBSCRIPT_BEYOND_COUNT Индекс больше, чем количество элементов в коллекции

SUBSCRIPT_OUTSIDE_LIMIT Индекс не принадлежит допустимому диапазону значений индекса

VALUE_ERROR Значение индекса равно NULL или не может быть преобразовано в целое

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

Управление транзакциями в PL/SQL

Транзакция — логическая единица работы, результат которой сохраняется при помощи оператора COMMIT или отменяется оператором ROLLBACK. Транзакция неявно начинается с первого оператора SQL, выполненного после последнего оператора COMMIT или ROLLBACK (или с начала сеанса), или продолжается после ROLLBACK TO SAVEPOINT. PL/SQL содержит ряд операторов для управления транзакциями:

COMMIT — сохраняет все изменения, сделанные после последней операции COMMIT Или ROLLBACK, и освобождает все блокировки.

ROLLBACK — отменяет все изменения, сделанные после последней операции COMMIT Или ROLLBACK, и освобождает все блокировки.

ROLLBACK TO SAVEPOINT — отменяет все изменения, сделанные после установки указанной точки сохранения, и освобождает блокировки, которые были установлены в данном фрагменте кода.

SAVEPOINT — устанавливает точку сохранения, которая затем позволит выполнять частичный откат.

SET TRANSACTION — позволяет начать транзакцию в режиме «только для чтения» или «для чтения и записи», задать уровень изоляции или сопоставить текущей транзакции определённый сегмент отката.

LOCK TABLE — позволяет заблокировать всю таблицу базы данных в определённом режиме. Позволяет изменить обычно применяемую к таблице установку по умолчанию — блокировку на уровне строк.

Оператор COMMIT

Выполнение оператора COMMIT делает постоянными изменения, внесённые вашим сеансом в базу данных в рамках текущей транзакции. После выполнения COMMIT (фиксации транзакции) сделанные вами изменения станут видимыми для других сеансов и пользователей Oracle.

Синтаксис оператора COMMIT:

COMMIT [WORK] [COMMENT текст];

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

Следует помнить, что фиксация транзакции освобождает любые блокировки строк и таблиц, установленные вашим сеансом, например, для команды SELECT FOR UPDATE. Кроме того, удаляются все точки сохранения, установленные после последней операции COMMIT или ROLLBACK.

После того как изменения зафиксированы (COMMIT), их уже невозможно отменить при помощи оператора ROLLBACK. Примеры корректного использования оператора COMMIT:

COMMIT;

COMMIT WORK;

COMMIT COMMENT 'maintaining account balance'

Оператор ROLLBACK

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

Что касается SQL, оператор ROLLBACK обеспечивает возможность исправления возможных ошибок, например:

DELETE FROM orders;

«О нет, я хотел удалить только те заказы, которые были сделаны до мая 1995 года!!!». Нет проблем - просто выполните ROLLBACK. С точки зрения кодирования приложения смысл ROLLBACK в том, чо он позволяет при возникновении проблемы ернуться в исходное состояние — начать всё «с чистого листа».

Синтаксис оператора ROLLBACK:

ROLLBACK [WORK] [TO [SAVEPOINT] имя_точки_сохранения];

Можно использовать оператор ROLLBACK в одном из двух режимов: без указания параметров или с предолжением TO, указывающим точку сохранения, до которой следует откатить изменения. ROLLBACK без параметров отменяет все изменения, внесённые в рамках транзакции.

Форма ROLLBACK TO позволяет отменить все изменения и освободить все блокировки, сделанные после того, как была установлена точка сохранения с меткой имя_точки_сохранения.

Параметр имя_точки_сохранения — это необъявляемый идентификатор Oracle, который не может быть ни литералом (заключенным в кавычки), ни именем переменной.

Допустимы все нижеперечисленные примеры использования ROLLBACK:

ROLLBACK;

ROLLBACK WORK;

ROLLBACK TO begin_cleanup;

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

PL/SQL неявно генерирует точку сохранения непосредственно перед выполнением оператора INSERT, UPDATE или DELETE. И в случае неудачного исполнения оператора DML откат изменений автоматически осуществляется до данной неявной точки сохранения. Таким образом, отменяется только последний оператор DML.

Автономные транзакции

Определяя PL/SQL-юдок (анонимный блок, процедуру, функцию, пакетную процедуру, пакетную функцию, триггер базы данных) как автономную транзакцию, вы изолируете в этом блоке команды DML от контекста транзакции вызывающего приложения. Такой блок становится независимой транзакцией, которая запускается другой транзакцией, называемой главной по отношению к данной.

Во время выполнения блока автономной транзакции главная транзакция приостанавливается. Вы выполняете SQL-операции, фиксируете их или откатываете, затем возобновляете главную транзакцию.

Определить PL/SQL-блок как автономную транзакцию несложно:

в раздел объявлений включается директива:

PRAGMA AUTONOMOUS_TRANSACTION;

Данная директива указывает компилятору PL/SQL, что следует определить блок PL/SQL как автономный (независимый). Следующие виды PL/SQL-блоков могут быть определены как автономная транзакция:

* Анонимные блоки PL/SQL верхнего уровня (но не вложенные)

Функции и процедуры, определённые внутри пакета или являющиеся самостоятельными программами

Методы (функции и процедуры) объектного типа

Триггеры базы данных

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

Данная директива — единственное добавление в синтаксис языка, потребовавшееся для поддержки автономных транзакций в PL/SQL. COMMIT, ROOLBACK, команды DML использовались и ранее. Однако при исполнении внутри автономной транзакции эти операторы имеют другую область действия и видимости; необходимо явно включить COMMIT или ROLLBACK в свою программу.

Основы курсоров

Курсор – объект, обеспечивающий операции на уровне строк предложения языка PL/SQL.

Объявление курсора – указатель на область памяти, которая используется при применении курсора.

Курсор может быть любым допустимым предложением SELECT языка PL/SQL. Обычно курсор – основной блок предложений языка PL/SQL.

Они обеспечивают цикличный механизм оперирования данными в базе, при этом возможно обновление данных с помощью FOR UPDATE.

При использовании FOR UPDATE осуществляется блокировка всех строк, участвующих в запросе, она сохраняется до закрытия курсора. Курсор может возвращать: одну строку, массив строк, или пустое значение.

Определение курсора:

CURSOR <имя> IS < оператор SQL>

Пример:DECLARE

CURSOR get_order IS SELECT * FROM orders;

Определение и использование курсоров

2 основных подхода для работы с курсорами:

1. Курсору можно передать параметры. Параметры задаются в момент открытия курсора.

2. Можно определять или не определять тип возвращаемого значения. Возможный тип: указанная запись, строка БД, отдельная переменная.

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

После объявления курсор может быть открыт, строки выбраны, состояние проверено и он может быть закрыт. Курсор может иметь любое имя, рекомендуется использовать префикс get_ или постфиксы _cur, _loop.

В имени курсора рекомендуется использовать имя таблицы или как-то по-другому ссылаться на нее.

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

Пример:

  1. Выборка всех записей из таблицы.

CURSOR get_order IS SELECT * FROM orders;

  1. Выборка некоторых столбцов для одного заказа.

CURSOR get_orders_1 (p_ord_num orders.ord_num %TYPE) IS

SELECT <имена столбцов> FROM orders WHERE ord_num = p_ord_num;

  1. Получение полной строки для определения номера

CURSOR get_items (p_item_no items.item_no %TYPE) IS SELECT * FROM items WHERE item.no=p_item_no; RETUN items % ROWTYPE;

  1. Выборка строк возвращается значение одного столбца

CURSOR get_items_1 (p_item_no items.item_no %TYPE) RETUN items.item_name % TYPE IS SELECT item_name FROM items WHERE item.no=p_item_no;

Возвращается таблица из одной записи с одним полем

Методы курсора

Метод

Возвращаемое значение

Описание

IS OPEN

True/false

Открыт ли курсор

FOUND

True/false

True, если строка найдена

NOTFOUND

True/false

True, если строка не найдена

ROWCOUNT

NUMBER

Порядковый номер полученной строки (начиная с 1

Пример:

IF orders_cur % FOUND THEN

OPEN items_cur (orders_cur.order_no),

LOOP

EXIT WHEN items_cur % NOTFOUND;

<…>

END LOOP;

END IF;

Пример: IF items_cur % ISOPEN THEN CLOSE items_cur;

END IF;

Циклическая обработка курсоров

Простейший способ использования – циклы FOR. При этом открытие, выборка и закрытие курсора происходит автоматически в области видимости цикла FOR.

Возвращаемая переменная – строка определяется неявно, на нее нельзя сослаться вне области цикла. Курсор может принимать параметры. Допускается объявление курсора в разделе DECLARE или в теле цикла.

Использование неявных курсоров

Предназначены для получения первой строки, если же будут найдены несколько строк, следовательно должна произойти обработка исключений (exception TOO_MANY_ROWS). Если в SELECT организовать чтение в переменную типа столбца, то это будет неявный курсор.

Пример:

DECLARE

get_tables_rec all_tables%ROWTYPE;

local_owner all_tables.owner%TYPE:=”DEMO”;

local_table all_tables.name%TYPE:=”COSTOMER”;

BEGIN

SELECT * INTO get_tables_rec FROM all_tables WHERE owner=local.owner AND name=local.table;

<действия>

END;

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

2.4. Исключительные ситуации.

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

Некоторые исключительные ситуации (ROW_NO_FOUND) являются событиями нормальной обработки. Исключительные ситуации типа VALUE_ERROR – программная ошибка или неожиданное событие.

Если не установлен обработчик исключительной ситуации управление передается блоку верхнего уровня.

Исключительная ситуация поднимается вверх по вложенным блокам, пока не встретит обработчик, иначе передается управление вызывающему контексту (SQL PLUS)

Если ожидаемая исключительная ситуация не включена в список или неизвестна какая исключительная ситуация может возникнуть, либо необходимо обработать все исключительные ситуации, тогда применяют выражение: WHEN OTHERS THEN

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

Предотвращение зацикливания в обработчике исключительных ситуаций

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

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

EXCEPTION

WHEN OTHERS THEN

BEGIN

if get_cursor%TYPE isopen then close get_cursor;

end if;

EXCEPTION

WHEN OTHERS THEN NULL

END;

END;

Всегда следует устанавливать обработчик исключительных ситуаций для самого внешнего блока PL/SQL. При этом пользователь должен получать короткое сообщение об ошибке.

Пользовательские исключительные ситуации

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

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

Пример: использования директивы

SET SERVEROUTPUT ON

DECLARE

invalid_num_format exception;

PRAGMA exception_init (invalid_num_format, -1481)

num_fmt constant varchar(3):=”aaa”;

x number(10);

BEGIN

dbms.output.enable;

SELECT to_number (‘999’, num, fmt) INTO x FROM dnal;

EXCEPTION

WHEN invalid_num_format THEN

dbms_output.put_line (“Вы идиот”);

WHEN OTHERS THEN

Dbms_output.put_line(“Все равно вы идиот”);

END;

END;

Обработка исключительных ситуаций внутри программы

Рекомендуется программировать блоки с обработчиками исключительных ситуаций для всех выражений PL/SQL. Это позволит продолжить обработку и выполнение PL/SQL оператора в случае возникновения ошибки. Особо важно это для циклов. Если обработку не предусмотреть, то всякое исключение приведет к немедленному завершению цикла и передаче управления обработчику исключительной ситуации охватывающего блока.

LOOP //

FETCH master_cursor INTO master_rec;

EXIT WHEN master_cursor%NOTFOUND;

BEGIN

DELETE FROM child_table WHERE

master_f_key=master_rec.master_p_key;

EXCEPTION WHEN OTHERS THEN

status:=SQLCODE;

вывод сообщения об ошибке;

END;

END LOOP;

Процедуры, функции и параметры. Локальные модули. Перегрузка модулей.

Реализация всякого развитого языка программирования предполагает вошожность создания и поддержки процедур и функции Обычно процедуры и функции, связанные единым целевым назначением объединяются в пакеты Особенностью Oracle является то, что процедуры и функции являются объ­ектами базы данных. Это означает, что их описания хранятся в словаре данных, а собственно код хранится не в библиоте­ках файловой системы, как это принято в языках програм­мирования, а непосредственно в базе данных В Oracle функ­ции отличаются от процедур тем, что функции возвращают в вызывающую среду одно значение соответствующего типа данных, а процедура не возвращает ничего. Oracle также поддерживает специальный тип процедур — триггер, кото-рып рассматривается как самостоятельный объект базы дан­ных. Дуально триггеры будут рассмотрены ниже, а коротко триггер можно охарактеризовать как автоматически запускаемую сервером процедуру.

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

Для выполнения стандартного набора типовых действий по преобразованию данных в Oracle предусмотрен достаточно богатый набор встроенных в PL/SQL функций, которые кратко описываются ниже.

SQL-функции Oracle

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

Каждая база данных Oracle имеет специальную обще доступную таблицу словаря данных с именем DUAL. Эта таблица содержит один столбец DUMMY и одну строку со значением X. Один из наиболее распространенных вариантов использования таблицы DUAL — возврат результатов SQL функций. Однострочные символьные функции

Часто при выполнении запросов и хранимых процедур возникают задачи, связанные с выполнением различных опе­рации со строками данных. Однострочные символьные функ­ции получают символьные исходные данные, а возвращать могут как символьные, так и числовые значения.

Функции, устанавливающие соответствие числовых кодов и символов

Функция СHR.(номер_символа) возвращает символ, который имеет соответствующее значение параметра номер_символа в используемом коде (обычно ASCII).

Пример применения функции представлен в листинге

Функция АБС11(символ) возвращает числовое значение (номер) символа, заданного параметром символ. Пример применения функции представлен в листинге.

Преобразования букв различных слов строки

Функция INITCAP(строка) преобразует каждую первую букву слов параметра строка в прописную, а все последующие — в строчные. Пример применения функции представлен в листинге.

Функция LOWER(строка) преобразует каждую букву параметра строка в строчную. Пример применения функции представлен в листинге.

Функция UPPER(строка) преобразует каждую букву параметра строка в прописную. Пример применения функ­ции представлен в листинге.

Символьные функции усечения и дополнения строк

Функция LPAD(строка_7, число_символов [,сим-вол_наполнитель\) возвращает значение параметра строка_1, дополненное слева до числа символов, которое задано пара­метром число _символов, символом-наполнителем, заданным параметром символ_наполнитель. По умолчанию символом-наполнителем является пробел.

Функция RPAD(строка_7, число_символов [,сим-вол „наполнитель]) возвращает значение параметра строки_1, дополненное справа до числа символов, которое задано пара­метром число_символов, символом-наполнителем, заданным параметром символ_наполнитель. По умолчанию символом-наполнителем является пробел.

Функция LIRTU(строка_J [,строка_шаблон]) возвра­щает усеченное слева значение параметра строка_1. Из строки параметра строка_1 символы удаляются слева до тех пор, пока удаляемый символ входит в множество символов параметра строка_шаблон. По умолчанию строка_шаблон состоит из символа пробела.

Функция RTRIMстрока_7 [,строка_шаблон]) возвра­щает усеченное справа значение параметра строка_1. Из строки параметра строка_1 символы удаляются справа до тех пор, пока удаляемый символ входит в множество символов параметра строка_шаблон. По умолчанию строка_шаблон состоит из символа пробела.

Символьные функции преобразования строк

Функция TRANSLATE(строка_7, символы_поиска, символы_замень1) возвращает значение параметра строка_1, для которой выполнено следующее преобразование. Все вхо­ждения параметра символ_поиска замещены значением пара­метра символ_замены. Если в строке символы_поиска содер­жится больше символов, чем в строке символызамены, то символы, которым нет соогветствия, замещаются на пустой символ 0 о есть исключаются из результирующей строки). Функция Translate может применяться, в частности, для обработки текстов, подготовленных с использованием раз­личных раскладок клавиатур.

Функция REPLACE(строка, строка_поиска 1_трока_замеи{ения]) возвращает значение параметра строВсе вхождения параметра строка _поиска замещены значением параметра. строка__замещения. Если параметр строка_зсше-щения не задан, то все вхождения параметра строка_поиска удаляются.

Функции связанные с поиском вхождений подстрок

Функция SUBSTR(строка_7, позиция [,длина_под-строки}) возвращает подстроку параметра строка_1, начиная с позиции, заданной параметром позиция, и длиной, заданной параметром длина_подстроки. Если параметр дли-на_подстроки не задан, то возвращается подстрока до конца строки, заданной параметром строка_1.

Функция INSTR(строка_7, строка_поиска [,пози-ция_начала_поиска [,число_вхождений]]) возвращает позицию вхождения строки, задаваемой параметром строка_поиска, в строку, задаваемую параметром строка_1. Позиция начала поиска задается необязательным числовым параметром пози-ция_начала_поиска, а необязательный параметр число_вхож-дений задает требуемое число вхождений строки поиска в основную строку. Значения по умолчанию для необязатель­ных параметров 1. При отсутствии требуемого параметра вхождения строки поиска в основную строку функция воз­вращает значение 0.

Функция LENGTH(строка) возвращает длину строки, заданной параметром строка.

Числовые функции связанные с возведением в степень и логарифмированием

Функция ЕХР(числовой_аргумент) возвращает число е (основание натуральных логарифмов) в степени параметра числовой _аргумент.

Функция LN(числовой_аргумент) возвращает нату­ральный логарифм положительного параметра числовой_аргу-мент.

Функция POWER(основание, числовой_аргу мент) воз-вращает значение параметра основание в степени параметрам числовой_аргумент. Если параметр основание отрицательный, то параметр числовой_аргумент должен быть целым.

Функция LOG(основание, числовой _аргумент) воз­вращает логарифм по основанию, заданному параметром ос­нование, параметра числовой_аргумент. Параметр основание может быть любым положительным числом» за исключением 1, а параметр числовой_аргумент должен быть положитель­ным числом.

Тригонометрические функции

Функции SIN(числовой_аргумент),С08(числовой_аргумент), TAN(чucлoвoй_apгy-мeнm) возвращают соот­ветственно синус, косинус и тангенс параметра число-вой_аргумент. Параметр числовой_аргумент предполагается заданным в радианах.

Функции АSIN(числовой_аргумент) и АСОS(число вой_аргумент) возвращают соответственно арксинус и арк­косинус параметра числовой_аргумент. Параметр число­вой_аргу мент предполагается находящимся в диапазоне от -1 до 1. При вычислении функции с параметром числовой_аргу-мент вне указанного диапазона, выдается сообщение об ошибке. Функция АTAN(числовой_аргумент) возвращает арктангенс параметра числовой _аргумент.

Функции SINHчисловой_аргумент), СОSH(чнсло-вой_аргумент), TANH(чucлoвoй_apгyмeнm) возвращают соот­ветственно гиперболический синус, гиперболический косинус и гиперболический тангенс параметра числовой_аргумент.

Функция FLOOR(числовой_аргумент) возвращает наибольшее целое, меньшее или равное значению параметра числовой_аргумент.

Функция СЕIL(числовой_аргуметп) возвращает наи­меньшее целое, большее или равное значению параметра чи-словой__аргумент,

Функция _ВВ(числовой_аргумент) возвращает абсо­лютное значение числа, заданного параметром числовой_аргу-мент.

Функция ROUMD( дата [,формат\) округляет значе­ние параметра дата по шаблону, определяемому параметром формат. Если параметр формат опущен, то аргумент дата округляется до дней (время в начале дня устанавливается в полночь).

Функция TRANC( дата [,формат]) усекает значение параметра дата по шаблону, определяемому параметром формат. Если параметр формат опущен, то аргумент дата усекается до ближайшего дня (время в начале дня устанавли­вается в полночь).

Пакеты. Правила построения пакетов.

Пакет — это сгруппированные вместе элементы PL/SQL-кода. Пакеты представляют собой физическую и логическую структуру для организации программ и других элементов PL/SQL , таких как курсоры, типы и переменные. Они также предоставляют важные функциональные средства, такие как сокрытие логики и данных, определение глобальных данных (существующих на протяжении сеанса) и работу с ними.

Правила построения пакетов

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

Спецификация пакета

В спецификации пакета перечислены все доступные для использования в приложениях элементы пакета, а также приведена вся информация, которая необходима разработчику для использования этих элементов (эту информацию часто называют программным интерфейсом приложения (API — application programming interface)). Разработчик должен иметь возможность работать с элементами, приведёнными в спецификации, не обращаясь за разъяснениями по их использованию к коду тела пакета. Приведём некоторые правила создания спецификации пакета:

Вы можете объявлять элементы практически всех типов данных: числа, исключения, типы и коллекции, на уровне пакета (т.е. не внутри конкретной процедуры или функции пакета). Такие данные называются данными уровня пакета. При этом следует избегать объявления переменных в пакете, тогда как объявление констант вполне допустимо.

В спецификации (или теле) пакета нельзя объявлять курсорные переменные (переменные, определяемые на основе типа REF CURSOR). Курсорные переменные не могут сохранять своё значение на протяжении сеанса.

Вы можете объявлять практически любые виды структур данных, такие как коллекции, записи или тип REF CURSOR.

Вы можете объявлять в спецификации пакета процедуры и функции, но указывать можно только заголовок программы (все, что находится выше ключевого слова IS или AS).

Вы можете включать в спецификацию пакета явные курсоры. Возможны две формы явного курсора: объявление запроса может включать в себя SQL-запрос, или же запрос может быть спрятан в теле пакета, тогда в объявлении курсора будет присутствовать только предложение RETURN.

Если в спецификации пакета объявлены какие-то процедуры или функции, а также если курсорная переменная объявлена без SQL-запроса, то необходимо написать тело пакета, в котором будут реализованы эти элементы кода.

В спецификацию пакета можно включить предложение AUTHID, которое будет определять, в соответствии с какими привилегиями разрешены любые ссылки на объекты: владельца пакета (AUTHID DEFINER) или пользователя, вызывающего пакет (AUTHID CURRENT_USER).

После оператора END в спецификации пакета можно поместить необязательную метку с именем пакета, например:

END my_package;

Рассмотрим эти правила на примере очень простой спецификации пакета:

1 CREATE OR REPLACE PACKAGE favorites_pkg

2 AUTHID CURRENT_USER

3 IS

4 -- Две константы. Обратите внимание на то,

5 -- что им даны «говорящие» имена.

6

7 c_chocolate CONSTANT PLS_INTEGER := 16;

8 c_strawberry CONSTANT PLS_INTEGER := 29;

9

10 -- Объявление типа вложенной таблицы

11 NYPE codes_nt IS TABLE OF INTEGER;

12

13 -- Вложенная таблица объявляется на основе этого типа.

14 my_favorites codes_nt;

15

16 -- Тип REF CURSOR, возвращающий информацию из таблицы favorites.

17 TYPE fav_info_rct IS REF CURSOR RETURN favorites%ROWTYPE;

18

19 -- Процедура, которая получает список популярных товаров

20 -- (используя определённый ранее тип) и выводит

21 -- соответствующую информацию.

22 PROCEDURE show_favorites (list_in IN codes_nt);

23

24 -- Функция, которая возвращает всю информацию

25 -- из таблицы favorites о наиболее популярном элементе.

26 FUNCTION most_popular RETURN fav_info_rct;

27

28 END favorites_pkg; -- Метка конца для пакета

Как видите, спецификация пакета по структуре очень похоже на секцию объявлений PL/SQL-блока. Однако важным отличием является то, что спецификация пакета не может содержать никакого кода реализации.

Тело пакета

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

Спецификация пакета содержит объявление курсора с предложением RETURN/ Необходимо определить в теле пакета оператор SELECT.

Спецификация пакета содержит объявление процедуры или функции. Необходимо представить полную реализацию модуля в теле пакета.

Вам требуется исполнение кода в разделе инициализации тела пакета. Спецификация пакета не включает в себя исполняемый раздел (исполняемые операторы внутри конструкции BEGIN…END); вы можете исполнять код только в теле пакета.

По своей структуре тело пакета очень похоже не определение процедуры, но имеет и некоторые отличительные особенности:

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

Исполняемый раздел пакета принято называть разделом инициализации. Он может содержать код, который исполняется при создании в сеансе экземпляра пакета 1).

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

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

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

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

После оператора END можно поместить необязательную метку с именем пакета, например:

END my_package

Рассмотрим реализацию тела пакета favorites_pkg:

CREATE JR REPLACE PACKAGE BODY favorites_pkg

IS

-- Приватная переменная

g_most_popular PLS_INTEGER := c_strawberry;

-- Реализация процедуры

PROCEDURE show_favorites ( list_in IN codes_nt) IS

BEGIN

FOR indx IN list_in.FIRST .. list_in.LAST

LOOP

DBMS_OUTPUT.put_line (list_in (indx));

END LOOP;

END show_favorites;

-- Реализация функции

FUNCTION most_popular RETURN fav_info_rct

IS

retval fav_info_rct;

null_cv fav_info_rct;

BEGIN

OPEN retval FOR

SELECT *

FROM favorites

WHERE code = g_most_popular;

RETURN retval;

EXCEPTION

WHEN NO_DATA_FOUND THEN RETURN null_cv;

END most_popular;

END favorites_pkg; -- Метка окончания пакета

Правила вызова элементов пакета

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

Рассмотрим несколько примеров.

В данной спецификации пакета объявляются константа, исключение, курсор и несколько модулей:

CREATE OR REPLACE PACKAGE pets_inc

IS

max_pets_in_facility CONSTANT INTEGER := 120;

pet_is_sick EXCEPTION;

CURSOR pet_cur (pet_id_in IN pet.id%TYPE) RETURN pet%ROWTYPE;

FUNCTION next_pet_shots ( pet_id_in IN pet.id%TYPE) RETURN DATE;

PROCEDURE set_schedule (pet_id_in IN pet.id%TYPE);

END pets_inc;

Для ссылки на любой из этих объектов необходимо использовать имя пакета в качестве префикса перед именем объекта, а именно:

DECLARE

-- Объявляем константу на основе столбца Id таблицы pet.

c_pet CONSTANT pet.id%TYPE := 1099;

v_next_appointment DATE;

BEGIN

IF pets_inc.max_pets_in_facility > 100

THEN

OPEN pets_inc.pet_cur (c_pet);

ELSE

v_next_appointment := pets_inc.next_pet_shots (c_pet);

END IF;

EXCEPTION

WHEN pets_inc.pet_is-sick

THEN

pets_inc.set_schedule (c_pet);

END;

Существуют два правила использования элементов пакета:

При ссылке извне (во внешней программе) на элемент, определённый в спецификации пакета, необходимо использовать точечную нотацию в формате имя_пакета.имя_элемента.

При ссылке внутри пакета (в спецификации или теле) на элемент пакета указывать имя пакета не обязательно; PL/SQL автоматически интерпретирует ссылку как направленную на внутренний элемент пакета.

Данные пакета

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

Если данные пакета определены в теле пакета, то они сохраняют свои значения на протяжении сеанса, но доступ к ним разрешён только для элементов этого пакета (приватные данные).

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

Если процедура пакета открывает курсор, то он остаётся открытым и доступным на протяжении всего сеанса. Нет необходимости в определении курсора в каждой программе. Один модуль может открывать курсор, а другой — выполнять выборку. Кроме того, переменные пакета могут передавать данные из одной транзакции в другую, т.к. переменные привязаны к сеансу, а не к какой-то определённой транзакции.

Триггеры

Триггер (TRIGGER) — это хранимая процедура, которая запускается (автоматически выполняется) тогда, когда происходит связанное с триггером событие. Обычно события связаны с выполнением операторов INSERT, UPDATE или DELETE в некоторой таблице. Наличие триггеров открывает практически неограниченные возможности по формированию проверок полномочий и допустимости действий, связанных с модификацией данных.

Событие, управляющее запуском триггера, описывается в виде логических условий. Когда возникает событие, соответствующее условиям триггера, сервер Oracle автоматически запускает триггер, то есть интерпретирует код программы триггера, записанный на языке PL/SQL.

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

Триггер запускается при выполнении одной из трех операций изменения содержимого таблицы: INSERT, DELETE или UPDATE. Триггер может запускаться и несколькими операторами, но хотя бы один оператор из тройки должен быть обязательно указан в условии запуска триггера. Если перечень операторов, запускающих триггер, включает оператор UPDATE, то для условий срабатывания могут быть указаны конкретные изменяемые столбцы.

Код триггера может выполняться либо до, либо после тех операторов, которые инициировали запуск триггера. Например, если триггер запускается перед выполнением операции модификации данных для проверки полномочий пользователя на право выполнения операции, то, конечно, нужно использовать триггер с запуском до выполнения операции (с ключевым словом BEFORE). Если триггер применяется для формирования данных для аудиторской записи, то разумно использовать триггер с запуском после выполнения операции (с ключевым словом AFTER).

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

Чтобы создать триггер, необходимо иметь системные привилегии CREATE TRIGGER. Для создания триггера в схеме, отличной от текущей схемы пользователя, требуются системные привилегии CREATE ANY TRIGGER.

Оператор определения триггера Oracle использует следующий синтаксис:

CREATE [OR REPLACE] TRIGGER [имя_схемы.] имя_триггера {BEFORE | AFTER}

{INSERT | DELETE | UPDATE [OF имя_столбца [, имя_столбца…]] }

[OR {INSERT | DELETE | UPDATE [OF имя_столбца [, имя_столбца…]] }…] ON [имя_схемы.]{имя_таблицы | имя_представления}

[FOR EACH ROW] [WHEN условие]

Ключевое слово OR REPLACE указывает на безусловное замещение старого текста триггера. Если ключевое слово OR REPLACE не указано и триггер определен в системе, то замещения старого значения триггера не происходит, и возвращается сообщение об ошибке.

Ключевые слова BEFORE или AFTER указывают на выполнение кода триггера либо до, либо, соответственно, после операторов манипулирования данными, инициировавших запуск триггера.

Ключевые слова INSERT, DELETE или UPDATE определяют конкретный оператор, запускающий триггер. Для оператора UPDATE могут быть указаны столбцы, изменение которых запускает триггер. Если конкретные столбцы не указаны, то триггер запускает изменение любого столбца. Необязательное ключевое слово OR присоединяет дополнительный оператор, запускающий триггер.

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

Прежде чем перейти к примеру построения триггера, приведем некоторые дополнительные сведения об обработке исключительных ситуаций в Oracle. Для подключения к механизму обработки ошибок пользовательских точек входа применяется процедура RAISE_APPLICATION_ERROR. С ее помощью можно обработать до 1000 определяемых пользователем ошибок с номерами в диапазоне от -20000 до -20999.

Вызов процедуры RAISE_APPLICATION_ERROR приводит к генерации исключительной ситуации и завершению выполнения вызвавшей процедуру программы (сравните с рассмотренным выше оператором PL/SQL RAISE). При этом в среду, вызвавшую программу, возвращается номер и текстовое сообщение о типе ошибки.

В отличие от процедур, функций и пакетов сервер Oracle не хранит код триггера в виде скомпилированного блока PL/SQL. При первом запуске триггера его код считывается из словаря данных, компилируется, и скомпилированная версия сохраняется в области SGA. Поэтому для часто используемых триггеров целесообразно код, отвечающий за процедурную часть триггера, включать в хранимую процедуру, а в теле триггера оставлять только запись условий запуска и вызовы соответствующих процедур и функций.

Пример:

Создадим триггер работающий при изменении количества товара в таблице Товар. При этом в таблице Склад меняется общее количество товара.

create or replace trigger trigger1 after update of Kol on Tovar for each row

begin

update Sklad set Kol=kol-:old.kol+:new.kol; --вычисляемые поля

where Kod_Group=:new.Kod_Group;

end trigger1;

На предложения языка SQL, включенные в код триггера Oracle, наложены следующие ограничения. Тело триггера не может включать в себя явное использование управляющих операторов COMMIT, ROLLBACK и SAVEPOINT, операторов языка определения данных CREATE, ALTER и DROP, операторов, управляющих разграничением доступа GRANT и REVOKE, а также неявное выполнение перечисленных операторов через вызовы процедур и функций.

Системные привилегии, определяющие права по работе с триггерами и процедурами

Для работы с процедурами в Oracle предусмотрены системные привилегии, приведенные в таблице.

Системная привилегия

Разрешаемые системной привилегией действия

CREATE ANY PROCEDURE

Разрешает пользователю создавать хранимую процедуру, функцию или пакет в любой схеме базы данных

DROP ANY PROCEDURE

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

EXECUTE ANY PROCEDURE

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

ALTER ANY PROCEDURE

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

Симметричные системные привилегии предусмотрены для работы с триггерами базы данных.

Системная привилегия

Разрешаемые системной привилегией действия

CREATE ANY TRIGGER

Разрешает пользователю создавать триггер БД, ассоцииро-ванный с таблицей любой схемы, в любой схеме БД.

CREATE TRIGGER

Разрешает пользователю создавать триггер базы данных в собственной схеме базы данных.

DROP ANY TRIGGER

Разрешает пользователю уничтожать произвольный триггер в любой схеме базы данных.

ALTER ANY TRIGGER

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

ДЕ16: Управление приложениями в PL/SQL.

Управление программным кодом и его анализ в базе данных

Представления словаря данных для программистов PL/SQL

Вывод информации о хранимых объектах

Вывод и поиск исходного кода

Защита кода хранимой программы

Как скрыть исходный код

Работа со скрытым кодом

Встроенная компиляция

Настройка, выполняемая администратором базы данных

Сравнение интерпретируемого и компилируемого режимов

Тестирование программ PL/SQL

Неэффективные технологии тестирования

Где найти дополнительную информацию

Отладка программ PL/SQL

Неупорядоченная отладка

Иррациональная отладка

Советы и стратегии отладки

Оптимизация программ PL/SQL

Анализ производительности PL/SQL

Трассировка выполнения кода

Повышение производительности приложения

Как избежать выполнения ненужного кода

Умение слушать

Использование пакетных данных для минимизации

SQL-обращений к базе данных

Использование предложения BULK COLLECT и оператора FORALL

Серверный и клиентский код

Подход состоит в том, чтобы делать в СУБД все, что возможно. Если требования выходят за пределы возможностей СУБД, я реализую соответствующие функции на языке Java вне СУБД. В этом случае особенности практически любой операционной системы скрываются. Мне все равно надо понимать, как работают "виртуальные машины" (Oracle или JVM) — надо знать используемые инструментальные средства, — но наиболее эффективная реализация соответствующих функций в конкретной ОС остается прерогативой создателей этих виртуальных машин.

Таким образом, зная лишь особенности работы одной "виртуальной ОС", можно создавать приложения, демонстрирующие отличную производительность и масштабируемость во многих операционных системах. Я не утверждаю, что можно полностью игнорировать базовую ОС, — просто разработчик приложений баз данных достаточно хорошо от нее изолирован, и ему не придется учитывать многие ее нюансы. Ваш АБД, отвечающий за поддержку СУБД Oracle, должен знать намного больше об особенностях базовой ОС (если не знает — найдите нового АБД!). При разработке клиент-серверного программного обеспечения, если основная часть кода вынесена из СУБД и виртуальной машины (наиболее популярной виртуальной машиной, вероятно, является Java Virtual Machine), разработчику придется учитывать особенности ОС сервера.

При разработке приложений баз данных я использую очень простую мантру:

• если можно, сделай это с помощью одного оператора SQL;

• если это нельзя сделать с помощью одного оператора SQL, сделай это в PL/SQL;

• если это нельзя сделать в PL/SQL, попытайся использовать хранимую процедуру на языке Java;

• если это нельзя сделать в Java, сделай это в виде внешней процедуры на языке С;

• если это нельзя реализовать в виде внешней процедуры на языке С, надо серьезно подумать, зачем это вообще делать...

Возможно использовать язык PL/SQL и его объектные типы для реализации того, что нельзя сделать в SQL. Язык PL/SQL существует давно, за ним стоит более тринадцати лет настройки, и нет другого языка, настолько тесно интегрированного с языком SQL и настолько оптимизированного для взаимодействия с SQL. Когда возможностей PL/SQL оказывается недостаточно, например, при доступе к сети, отправке сообщений электронной почты и т.п., мы будем использовать язык Java. Иногда мы будем решать определенные задачи с помощью языка С, но обычно лишь в тех случаях, когда программирование на С — единственно возможный вариант или когда обеспечиваемая компилятором С скорость работы программы действительно необходима. Во многих случаях сейчас последняя причина отпадает при использовании компиляции в машинные коды программ на языке Java (возможности преобразовать байт-код Java в специфический объектный код операционной системы для данной платформы). Это обеспечивает программам на Java такую же скорость работы, как и у программ на языке С.

Подход с использованием принципа черного ящика

Есть предположение, основанное на опыте, почему так часто разработка приложений баз данных заканчивается неудачно. Позвольте уточнить, что к разряду неудавшихся разработок я отношу также проекты, официально не признанные неудавшимися, но потребовавшие на разработку и внедрение намного больше времени, чем планировалось первоначально, поскольку пришлось их существенно "переписывать", "перепроектировать" или "настраивать". Лично я такие не завершенные в строк проекты считаю неудавшимися: очень часто их вполне можно было завершить вовремя (и даже досрочно).

Наиболее типичной причиной неудачи является нехватка практических знаний по используемой СУБД — элементарное непонимание основ работы используемого инструментального средства. Подход по принципу "черного ящика" требует осознанного решения: оградить разработчиков от СУБД. Их заставляют не вникать ни в какие особенности ее функционирования. Причины использования этого подхода связаны с опасениями, незнанием и неуверенностью. Разработчики слышали, что СУБД — это "сложно", язык SQL, транзакции и целостность данных — не менее "сложно". Решение: не заставлять никого делать что-либо "сложное". Будем относиться к СУБД, как к черному ящику, и найдем инструментальное средство, которое сгенерирует необходимый код.

Изолируем себя несколькими промежуточными уровнями, чтобы не пришлось сталкиваться непосредственно с этой "сложной" СУБД.

Такой подход к разработке приложений баз данных я не мог понять никогда. Одна из причин, почему мне трудно это понять, состоит в том, что для меня изучение языков Java и С оказалось намного сложнее, чем изучение основ работы СУБД. Я сейчас очень хорошо знаю языки Java и С, но для их освоения мне понадобилось намного больше практического опыта, чем для достижения соответствующего уровня компетентности при использовании СУБД. В случае СУБД необходимо знать, как она работает, но детали знать необязательно. При программировании на языке С или Java, необходимо, например, знать все особенности используемых компонентов; кроме тою, это очень большие по объему языки.

Еще одна причина — то, что при создании приложений базы данных самым важным компонентом программного обеспечения является СУБД. Для успешной разработки необходимо учитывать это и доводить до сведения разработчиков, постоянно обращая на это их внимание. Много раз я сталкивался с проектами, где придерживались прямо противоположных воззрений.

Вот типичный сценарий такого рода разработки.

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

• Команда разработчиков ни одного часа не изучала СУБД Oracle и не имела никакого опыта работы с ней. Многие разработчики вообще впервые сталкивались с СУБД.

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

Не сумев обеспечить нужную производительность, разработчики обращались за помощью ко мне. Особенно показателен один случай. Я не мог вспомнить точный синтаксис новой команды, которую надо было использовать, и попросил руководство SQL Reference. Мне принесли экземпляр из документации по СУБД Oracle версии 6.0, хотя разработка велась на версии 7.3, через пять лет после выхода версии 6.0! Ничего другого для работы у них не было, но это вообще никого не беспокоило. Хотя необходимое им для трассировки и настройки инструментальное средство в то время вообще не существовало. Хотя за пять лет, прошедших после написания имевшейся у них документации, были добавлены такие средства, как триггеры, хранимые процедуры, и многие сотни других. Несложно понять, почему им потребовалась помощь, гораздо труднее было решить их проблемы.

Странная идея о том, что разработчик приложения баз данных должен быть огражден от СУБД, чрезвычайно живуча. Многие почему-то считают, что разработчикам не следует тратить время на изучение СУБД. Неоднократно приходилось слышать: "СУБД Oracle — самая масштабируемая в мире, моим сотрудникам не нужно ее изучать, потому что СУБД со всеми проблемами справится сама". Действительно, СУБД Oracle — самая масштабируемая. Однако написать плохой код, который масштабироваться не будет, в Oracle намного проще, чем написать хороший, масштабируемый код. Можно заменить СУБД Oracle любой другой СУБД — это утверждение останется верным. Это факт: проще писать приложения с низкой производительностью, чем высокопроизводительные приложения. Иногда очень легко создать однопользовательскую систему на базе самой масштабируемой СУБД в мире, если не знать, что делаешь. СУБД — это инструмент, а неправильное применение любого инструмента может привести к катастрофе. Можно, конечно, и так, но это неправильное использование инструмента, и результат вас не порадует. Аналогичные результаты будут и при игнорировании особенностей используемой СУБД.

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

Подводя итоги: СУБД — это краеугольный камень приложения. Если она не работает как следует, все остальное не имеет значения. Если плохо работает черный ящик, что с ним делать? Его нельзя исправить, нельзя настроить (поскольку непонятно, как он устроен), и такую позицию вы выбрали сами. Но есть и другой подход, который я отстаиваю: разберитесь в используемой СУБД и принципах ее работы, поймите, что она может делать, и используйте весь ее потенциал.

ДЕ17: Объектно-ориентированные возможности PL/SQL

Многие из нас с появлением Oracle9i Database и далее Oracle10g Database начали активно разрабатывать приложения с помощью объектно-ориентированного PL/SQL. Однако вскоре выяснилось, что корпорация Oracle не полностью реализовала возможности присущие объектно-ориентированным языкам. В результате многие разработчики приложений на Oracle Database "охладели" к объектным возможностям PL/SQL.

Предлагается ряд решений проблем, с которыми сталкиваются разработчики, Oracle9i PL/SQL позволяет реализовывать развитую объектную модель.

Каждый раздел статьи сопровождается исходными текстами скриптов, демонстрирующими соответствующий подход. Все скрипты запускались и проверялись с помощью последней доступной на текущий момент версии Oracle10g Database - 10.1.0.2 Скрипты тестировались на следующей версии: Oracle 10.1.0.2 Enterprise Edition for linux x86 (Intel Xeon)

С этой проблемой PL/SQL-программисты сталкиваются наиболее часто. Проблема связана с тем, что в PL/SQL отсутствует синтаксическая конструкция для вызова метода типа-предка. В случае порождения нового объектного типа от родительского, виртуальный метод переопределеяется, далее в этом порожденном типе нам необходимо вызвать данный метод типа-предка.

Например: пусть у нас есть класс t_ParentType в котором определен метод getName:

------------------------------------------------------------

--спецификация объектного типа t_ParentType: -

------------------------------------------------------------

create or replace type t_ParentType as object

(

v_Field1 varchar2(32),

member function getName return varchar2

)

not final;

------------------------------------------------------------

--тело объектного типа t_ParentType: -

------------------------------------------------------------

create or replace type body t_ParentType as

member function getName return varchar2 is

begin

return self.v_Field1;

end;

end;

Теперь мы определяем объектный тип t_ChildType, который является наследником t_ParentType. В типе t_ChildType метод getName является виртуальным и переопределен. Для этого использовано ключевое слово OVERRIDING:

----------------------------------------------------------

--Спецификация объектного типа t_ChildType, -

--который является наследником : t_ParentType -

--Внимание: метод getName переопределен -

------------------------------------------------------------

create or replace type t_ChildType under t_ParentType

(

v_Field2 varchar2(64),

overriding member function getName return varchar2

)

not final;

В реализации метода getName попытаемся вызвать унаследованный метод getName (объектного типа t_ParentType)

------------------------------------------------------------

--Тело объектного типа t_ChildType, -

--в методе getName необходимо вызвать унаследованный метод -

------------------------------------------------------------

create or replace type body t_ChildType is

overriding member function getName return varchar2 is

begin

return (???) getName || ' ' || v_Field2; -- как вызвать

-- метод предка ???

end;

end;

Таким образом, выясняется, что в PL/SQL нет синтаксической конструкции, для того чтобы сослаться на метод типа-предка.

В объектно-ориентированных языках для этого существуют специальные языковые конструкции. В Java это ключевое слово super (супер-класс), в Object Pascal - Inherited. Данный механизм обеспечивает доступ к унаследованной логике и устраняет избыточность кода. Документация по языку PL/SQL (Oracle10g Rel.1 PL/SQL User's Guide and Reference, Oracle10g Rel.1 Application Developer's Guide - Object-Relational Features) хранит по этому поводу молчание.

Для решения этой проблемы предлагается следующий алгоритм:

Создается экземпляр типа-предка

Затем копируется в него содержимое полей текущего экземпляра

Происходит вызов нужного метода экземпляра типа-предка

Поля экземпляра типа-предка копируются в текущий экземпляра.

Модифицируем исходный текст для реализации этого подхода, добавив в родительский тип копирующий конструктор:

------------------------------------------------------------

--спецификация объектного типа t_ParentType, -

--добавлен копирующий конструктор -

------------------------------------------------------------

create or replace type t_ParentType as object

(

v_Field1 varchar2(32),

--копирующий конструктор:

constructor function t_ParentType(v_pObject in out nocopy t_ParentType)

return self as result,

member function getName(self in out nocopy t_ParentType)

return varchar2

)

not final;

------------------------------------------------------------

--тело объектного типа t_ParentType -

------------------------------------------------------------

create or replace type body t_ParentType is

constructor function t_ParentType(v_pObject in out nocopy t_ParentType)

return self as result is

begin

self.v_Field1 := v_pObject.v_Field1;

return;

end;

member function getName(self in out nocopy t_ParentType)

return varchar2 is

begin

return self.v_Field1;

end;

end;

В типе-потомке нам также будет необходим метод присваивания, который будет копировать все поля переменной экземпляра типа в текущий экземпляр, - назовем его assign. Далее добавим функцию inherited_getName, которая будет реализовывать алгоритм вызова функции getName родительского типа t_ParentType. Фактически метод inherited_getName представляет собой оболочку для метода getName типа-предка t_ParentType.

------------------------------------------------------------

--Спецификация объектного типа t_ChildType, -

--который является наследником : t_ParentType -

--Добавлен метод присваивания - assign -

------------------------------------------------------------

create or replace type t_ChildType under t_ParentType

(

v_Field2 varchar2(64),

constructor function t_ChildType(v_pField1 varchar2,

v_pField2 varchar2)

return self as result,

--метод для вызова унаследованного метода getName:

member function inherited_getName(self in out nocopy t_ChildType)

return varchar2,

--метод присваивания:

member procedure assign(self in out nocopy t_ChildType,

v_pObject in out nocopy t_ChildType),

overriding member function getName(self in out nocopy t_ChildType)

return varchar2

)

not final;

------------------------------------------------------------

--Тело объектного типа t_ChildType -

------------------------------------------------------------

create or replace type body t_ChildType is

constructor function t_ChildType(v_pField1 varchar2,

v_pField2 varchar2)

return self as result is

begin

self.v_Field1 := v_pField1;

self.v_Field2 := v_pField2;

return;

end;

member function inherited_getName(self in out nocopy t_ChildType)

return varchar2 is

v_xInheritedObject t_ParentType; --экземпляр объекта-предка

v_xRes varchar2(32);

begin

-- создаем экземпляр предка с помощью копирующего конструктора

v_xInheritedObject := new t_ParentType(self);

-- вызываем метод getName класса-предка

v_xRes := v_xInheritedObject.getName;

-- в общем случае вызов метода предка мог изменить поля

self.assign(v_xInheritedObject);

-- поэтому необходимо обратно скопировать их в текущий объект (self)

return v_xRes;

end;

----------------------------------------------------------

-- метод присваивания: -

-- просто копируем все поля-объекта источника в текущий -

-- экземпляр (self) -

----------------------------------------------------------

member procedure assign(v_pObject in out nocopy t_ChildType) is

begin

self.v_Field1 := v_pObject.v_Field1;

self.v_Field2 := v_pObject.v_Field2;

end;

----------------------------------------------------------

-- переопределенный метод getName: -

-- через вызов inherited_getName происходит обращение к -

-- унаследованному методу getName -

----------------------------------------------------------

overriding member function getName(self in out nocopy t_ChildType)

return varchar2 is

begin

return inherited_getName || '-' || v_Field2;

end;

end;

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

Например: в следующей иерархии классов: t_ParentType -> t_ChildType -> t_SubChildType для вызова метода произвольного типа-предка можно использовать следующие правило: к имени метода добавляется цифра - номер уровня в иерархии. В этом случае имена методов-оболочек соответственно будут выглядеть следующим образом:

getName0->getName1->getName2

Вышеописанная методика демонстрируется в данном примере.

Наследование конструкторов

Очередная трудность связана с тем, что в PL/SQL не поддерживает прямой вызов унаследованного конструктора. (Проще говоря, конструкторы базового типа не наследуются!). Например: пусть у нас есть класс t_ParentType в котором определен пользовательский (user-defined) конструктор:

----------------------------------------------------------------

--спецификация объектного типа t_ParentType: -

----------------------------------------------------------------

create or replace type t_ParentType as object

(

v_Field1 varchar2(32),

constructor function t_ParentType(v_pName varchar2)

return self as result

)

not final;

----------------------------------------------------------------

--тело объектного типа t_ParentType: -

----------------------------------------------------------------

create or replace type body t_ParentType as

constructor function t_ParentType(v_pName varchar2)

return self as result is

begin

self.v_Field1 := v_pName;

return;

end;

end;

Теперь мы определяем объектный тип t_ChildType, который является наследником t_ParentType. В типе t_ChildType также определен пользовательский конструктор:

----------------------------------------------------------------

--Спецификация объектного типа t_ChildType, -

--который является наследником : t_ParentType -

----------------------------------------------------------------

create or replace type t_ChildType under t_ParentType

(

v_Field2 varchar2(64),

constructor function t_ChildType(v_pName varchar2,

v_pDescription varchar2)

return self as result

);

В реализации конструктора типа t_ChildType попытаемся вызвать унаследованный конструктор:

----------------------------------------------------------------

--Тело объектного типа t_ChildType -

--в конструкторе необходимо вызвать унаследованный конструктор -

----------------------------------------------------------------

create or replace type body t_ChildType is

constructor function t_ChildType(v_pName varchar2,

v_pDescription varchar2)

return self as result is

begin

t_ParentType(v_pName => v_pName);

self.v_Field2 := v_pDescription;

return;

end;

end;

Выясняется, что сделать это не удается:

liNE/COL ERROR

-------- -----------------------------------------------------------------

6/5 PLS-00306: wrong number or types of arguments in call to

'T_PARENTTYPE'

Итак: как же вызвать конструктор родительского типа, чтобы не дублировать уже реализованный в нем код?

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

----------------------------------------------------------------

--Спецификация объектного типа t_ChildType, -

--который является наследником : t_ParentType -

--добавлен метод присваивания assign -

----------------------------------------------------------------

create or replace type t_ChildType under t_ParentType

(

v_Field2 varchar2(64),

constructor function t_ChildType(v_pName varchar2,

v_pDescription varchar2)

return self as result,

member procedure assign(self in out nocopy t_ChildType,

v_pObject in out nocopy t_ParentType),

member function getName

return varchar2

);

----------------------------------------------------------------

--Тело объектного типа t_ChildType -

--в конструкторе вызывается конструктор базового типа -

----------------------------------------------------------------

create or replace type body t_ChildType is

constructor function t_ChildType(v_pName varchar2,

v_pDescription varchar2)

return self as result is

--экземпляр объекта-предка

v_xInheritedObject t_ParentType;

begin

--вызов конструктора базового типа

v_xInheritedObject := new t_ParentType(v_pName => v_pName);

-передача данных текущему экземпляру

self.assign(v_xInheritedObject);

-

self.v_Field2 := v_pDescription;

return;

end;

--------------------------------------------------------------

--метод присваивания экземпляра базового типа текущему -

--оьъекту (self) -

--------------------------------------------------------------

member procedure assign(self in out nocopy t_ChildType,

v_pObject in out nocopy t_ParentType) is

begin

self.v_Field1 := v_pObject.v_Field1;

end;

member function getName

return varchar2 is

begin

return self.v_Field1 || ' - ' || self.v_Field2;

end;

end;

Вышеописанная методика демонстрируется в данном примере.

Реализация констант-атрибутов типа

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

create or replace type t_ParentType as object

(

v_Name varchar2(50),

static v_Const varchar2(32) := 'Scott Tiger'

);

Увы, мы получаем ошибку:

ORA-06545: PL/SQL: compilation error - compilation aborted

ORA-06550: line 5, column 12:

PLS-00103: Encountered the symbol "V_CONST" when expecting

one of the following:

function procedure

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

Следующий листинг реализует данный подход:

--------------------------------------------------------------

--Значение данного типа должен иметь атрибут объектного типа -

--------------------------------------------------------------

create or replace type t_DictConst as object

(

v_Id number(9),

v_Name varchar2(50),

v_Code varchar2(15),

v_Description varchar2(250)

);

--------------------------------------------------------------

--Спецификация вспомогательного пакета для типа t_ParentType:-

--функция getConst возвращает объект типа t_DictConst -

--------------------------------------------------------------

create or replace package serv$ParentType is

function getConst return t_DictConst;

end;

--------------------------------------------------------------

--Тело пакета: объект-константа формируется в процедуре init -

--------------------------------------------------------------

create or replace package body serv$ParentType is

v_gDictConst t_DictConst;

function getConst return t_DictConst is

begin

return v_gDictConst;

end;

procedure init is

begin

v_gDictConst := new t_DictConst(1,'Scott Tiger',

'01','Scott Tiger - Oracle demo-user');

end;

begin

init;

end;

Следующий объектный тип реализует статический метод, который возвращает объект-константу:

--------------------------------------------------------------

--Спецификация объектного типа t_ParentType -

--Статический метод возвращает константу -

--------------------------------------------------------------

create or replace type t_ParentType as object

(

v_Name varchar2(50),

static function getConst return t_DictConst

);

create or replace type body t_ParentType is

static function getConst return t_DictConst is

begin

return serv$ParentType.getConst;

end;

end;

Заключение

Мы рассмотрели методы решения наиболее часто встречающихся проблем при использовании объектно-ориентированных возможностей PL/SQL. Конечно, многие проблемы могут быть решены только самими разработчика корпорации Oracle. Например, отсутствие защищенных полей объектного типа (так называемых private-полей), отсутствие поддержки интерфейсов и т.д. Будем надеяться, что в следующих версиях Oracle Database эти недоработки будут устранены.

ДЕ18: Взаимодействие Java и PL/SQL

Начиная с Oracle8i в состав РСУБД включается продукт под названием JServer, состоящий из следующих компонентов:

- виртуальная машина Java для Oracle (Java Virtual Machine, JVM), называемая Aurora, а также соответствующие ей среда выполнения и библиотеки классов Java;

- средства интеграции Java с PL/SQL и РСУБД Oracle;

- брокер объектных запросов (Object Request Broker, или Aurora/ORB) и тех­нология Enterprise JavaBeans (EJB);

- встроенный компилятор JServer Accelerator.

Виртуальная машина Java Aurora вызывает методы Java, которые также назы­ваются хранимыми Java-процедурами (Java Stored Procedure, JSP), и классы так, как если бы они хранились в базе данных. Кроме того, совместно используемая библиотека может содержать несколько подпрограмм (отсюда и термин «библиотека»), что также снижает издержки, поскольку требуется динамически загружать мень­шее количество файлов.

- Отдельное пространство памяти. Внешние процедуры Oracle выполняются в области памяти, независимой от процессов ядра базы данных. Поэтому, если внешняя процедура завершается аварийно, это не оказывает никакого влия­ния на память ядра — процесс extproc просто возвращает код ошибки ядру PL/SQL, а оно передает сообщение о ней приложению. Хотя возможность на­писания внешней процедуры, которая может вызвать сбой работы сервера Oracle, существует, создать ее не легче, чем процедуру PL/SQL.

- Полная поддержка транзакций. Внешние процедуры полностью поддержива­ют транзакции. Это означает, что они могут быть задействованы в текущей транзакции. Получив от PL/SQL информацию «контекста», процедура может возвращать в базу данных записи, выполнять SQL- или PL/SQL-запросы и инициировать исключения. Все это требует низкоуровневого программирова­ния с использованием интерфейса уровня вызовов РСУБД (Oracle Call Inter­face, О CI), но главное, что такая возможность существует.

Конфигурирование Oracle Net

Рассмотрим, как настраиваются параметры конфигурации, отвечающие эа выпол­нение внешних процедур и повышающие эффективность системы защиты.

Настройка параметров конфигурации листенера

Соединение между PL/SQL и совместно используемыми библиотеками обеспе­чивается средствами компоненты Oracle Net. Хотя стандартная конфигурация Oracle8i и более поздних версий предусматривает определенную поддержку внеш­них процедур, вы, скорее всего, не захотите ограничиться предлагаемыми по умол­чанию возможностями до тех пор, пока разработчики Oracle не усовершенствуют систему защиты.

На момент написания этой книги в Oracle по-прежнему недостаточно учиты­вался тот факт, что выполнение внешних процедур является слабым местом сис­темы защиты базы данных. В частности, удаленный несанкционированный поль­зователь может подключиться к базе данных через порт TCP/IP листенера (обычно это порт 1521) и без аутентификации запустить процесс extproc. Чтобы избежать этой проблемы, советуем запускать листенеры Oracle за брандмауэром и не ос­тавлять доступным порт листенера, предназначенный для соединения с Интерне­том или другой незащищенной сетью.

Для настройки параметров листенера требуется модифицировать файлы tnsna-mes.ora и listener.ora (вручную или с помощью Oracle Net Manager). Приведем пример простого файла listener.ora, в котором параметры листернеров, предназна­ченных для внешней процедуры и для базы данных, устанавливаются отдельно:

LISTENER -

(ADDRESS « (PROTOCOL - TCPHHOST « имя_хоста){PORT - 1521))

EXTPROC_LISTENER -

(ADDRESS - (PROTOCOL - IPCXKEY - кпюч_процедуры_ехЬргос))

SIDJ_ISTJ_ISTENER « (SIDDESC -

(GLOBAL_DBNAME - глобапьное_имя) (0RACLE_H0ME - каталог JJracle) (SID_NAME « SID)

)

SID_LIST EXTPROC_LISTENER -(SID_DESC -

(SID_NAME - имя_внешней_процедуры) (0RACLE_H0ME - каталогJJracle)

(EN VS«" EXTPROC__DLLS=ONLY: списон_файлов_совместно_используемых_объектов") (PROGRAM - extproc) )

Назначение параметров, использованных в этом файле, описано ниже.

- ключ__процедуры_ехЬргос — короткий идентификатор, используемый Oracle Net для того, чтобы отличить данный листенер от других листенеров, участвую­щих в межпроцессорном взаимодействии (InterProcess Communication, IPC). Это имя можно выбирать произвольным образом, поскольку программы нико­гда его «не увидят». По умолчанию при первой инсталляции Oracle Net на кон­кретной машине используется имя EXTPROC0. В списках ADDRESS файлов tnsna-mes.ora и listener.ora этот идентификатор должен быть одинаковым.

имя__хоста — имя или IP-адрес машины. Данный параметр не применяется к внешним процедурам, которые выполняют прослушивание только через IPC.

Каталог_oracleполный путь к каталогу, в котором установлена система Oracle (ORACLE_HOME), предположим /u01/app/orac!e/producr/9.2 (в UNIX) или же C:\ORACLE\ora92 (в Microsoft Windows). Обратите внимание, что имя каталога не заключено в кавычки и не завершается косой чертой.

SIDвнешней_процедуры — это произвольный уникальный идентификатор листе-нера внешней процедуры. В стандартной конфигурации Oracle для него ис­пользуется значение PLSExtProc.

глобальное Jimполностью определенное имя базы данных. Этот элемент не относится к внешним процедурам.

ENVS="EXTPROC_Dl_LS=ONLY: список_файлов_совместно__используемых_объектов"

В Oracle9i Release 2 предложение ENVS устанавливает переменные окружения для листенера. В нашем примере переменной EXTPR0CJ3LLS присваивается зна­чение, обеспечивающее максимальный уровень защиты, — ключевое слово ONLY. Оно разрешает выполнение только тех совместно используемых библиотек, которые перечислены в списке.

Ниже приведен пример установки переменных среды для системы Solaris:

(ENVS="EXTPR0C_DLLS=0NLY:/u01/app/oracle/admin/local/lib/extprocsh.so:/u01/app/ oracle/admi n/1ocal/1i Ь/RawdataToPri nter.so")

А вот пример для системы Windows XP:

(ENVS=,,EXTPR0C_DLLS=0NLY:c:\oracle\admin\local\1ib\extprocsh.dll:C:\oracle\admin\ 1ocal\1i b\RawDataToPri nter.dll")

Заметьте, что двоеточие в данном случае применяется не только в качестве разделителя элементов списка, но и для того, чтобы отделить букву, обозна­чающую диск (как это принято в MS-DOS). Хотя в этом списке названы всего две библиотеки, вы, конечно же, можете задавать любое их количество.

Если при работе с Oracle9i Release 2 вы не зададите список значений в предло­жении ENVS, Oracle позволит вызывать только те библиотеки, которые располага­ются в одном, специально предназначенном для них каталоге Oracle. Для системы Windows это будет каталог bin, а для системы UNIX — каталог lib. Когда в значе­нии переменной EXTPR0CJ3LLS не указано ключевое слово ONLY, но задан список файлов библиотек, помимо указанных библиотек для выполнения будут доступ­ны и те, что хранятся в подкаталогах каталога bin или lib.

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

Уровень защиты, установленный в данной конфигурации

Заданные нами параметры конфигурации с точки зрения обеспечения безопасно­сти дают следующие преимущества:

О системный администратор может запускать листенер внешней процедуры от имени пользователя с учетной записью, предоставляющей ограниченные пра­ва. По умолчанию листенер выполняется от имени учетной записи, запустив­шей сервер Oracle;

О функции листенера внешней процедуры ограничены приемом IPC-запросов от локальной машины (возможность получения межсетевых запросов, посту­пающих по протоколу TCP/IP, отсутствует).

Настройку параметров листенера можно продолжить, добавив в файл tnsna-mes.ora для базы данных, из которой производится вызов, следующую запись:

EXTPROC_CONNECTION DATA -(DESCRIPTION -

(ADDRESS - (PROTOCOL - I PC)(KEY - HfW4_npouenypu_extproc)) (CONNECT_DATA - (SID « SID процедуры extproc) (PRESENTATION - RO)) )

Большая часть этих установок вам уже знакома из приведенного выше описа­ния конфигурации листенера. Обратите внимание на то, что значения ключ_проце-дуры_ехЬргос и 5Ю_процецуры_ехЬргос в файле настройки листенера должны соот­ветствовать значениям в данном файле. Заданное нами значение параметра RE­PRESENTATION предназначено для повышения производительности, оно указывает серверу, который поддерживает прослушивание по нескольким протоколам, что клиент будет взаимодействовать с ним по протоколу RemoteOps (RO).

Внимательно проверьте права, предоставленные учетной записи дополнитель­ного листенера, особенно права на модификацию файлов операционной системы или файлов пользователя с учетной записью oracle. Кроме того, задав значения переменной окружения TNS_ADMIN в UNIX (в Microsoft она устанавливается в рее­стре), файлы tnsnames.ora и listener.ora листенера внешней процедуры можно по­местить в отдельный каталог.

Создание библиотеки Oracle

Инструкция CREATE LIBRARY определяет в словаре данных Oracle псевдоним файла внешней совместно используемой библиотеки, что позволяет исполняющему ядру PL/SQL найти эту библиотеку во время вызова. Создавать библиотеки разреше­но только администраторам и пользователям с правами CREATE LIBRARY или CREATE

ANY LIBRARY.

В общем случае инструкция CREATE LIBRARY имеет такой вид:

CREATE [ OR REPLACE ] LIBRARY имя библиотеки AS ' пугь_к_файлу' [ AGENT ,связь_базы<_данных_агентд"\;

Здесь имя_библиотеки — допустимый идентификатор PL/SQL. Это имя будет при­меняться в теле внешних процедур, которым нужно вызвать совместно исполь­зуемый объектный файл (DLL). Имя библиотеки не может совпадать с именем таблицы, объекта PL/SQL верхнего уровня или другим именем в основном про­странстве имен.

Параметр путь_к_файлу — полный путь к совместно используемому объектно­му файлу (DLL). В OracIe9i появилась возможность применять в параметре луть_ н_файлу переменные окружения. В частности, если переменная устанавливается до запуска листенера, ее можно указать в инструкции CREATE LIBRARY. Например:

CREATE LIBRARY extprocshellJib AS '${ORACLE_HOME}/lib/extprocsh.so'; --UNIX CREATE LIBRARY extprocs hell Jib AS 'XORACLE^HOMEXXbinXextprocsh.dlT: --Microsoft

Это удобно с точки зрения переносимости сценария, хотя, возможно, не очень хорошо с точки зрения безопасности.

Имеется и другой способ обеспечить возможность применения переменных при создании библиотек, а именно путем добавления их в список значений пара­метра ENVS листенера внешней процедуры. Так, в системе UNIX, для того чтобы задействовать переменную MYLIBS, можно использовать такую строку:

(ENVS="EXTPROC_DLLS=ANY.MYLIBS-/usr/local/extproclib-)

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

CREATE LIBRARY extprocshellJ 1b AS '${MYLlBS}/extprocsh.so*; --UNIX

Практика показала, что помещать переменную в список значений параметра ENVS и ссылаться на нее при создании библиотеки можно только в системе UNIX (в Microsoft Windows XP это не работает).

Возможно, вы захотите установить в системе UNIX еще одну переменную — LD_LIBRARY_PATH или ее эквивалент, чтобы предоставить внешней процедуре путь

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

Параметр AGENT 'связь_базы_данных_агента' в инструкции CREATE LIBRARY — это не­обязательный параметр, задающий соединение с базой данных (в версии Oracle9i и выше), к которой имеет доступ владелец библиотеки. Это соединение должно быть ассоциировано с именем сервиса для внешней процедуры. Предложение AGENT позволяет внешней процедуре выполняться на другом сервере базы данных, хотя физически она может располагаться на том же компьютере.

Пользуясь инструкцией CREATE LIBRARY, имейте ввиду следующее:

О инструкция должна выполняться администратором базы данных либо пользо­вателем с правами CREATE LIBRARY или CREATE ANY LIBRARY;

О как большинство других объектов базы данных, библиотека принадлежит кон­кретному пользователю (схеме) Oracle, который автоматически получает право на ее выполнение и может предоставлять и отменять такое право для других пользователей;

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

О выполняя инструкцию CREATE LIBRARY, Oracle не проверяет, существует ли ука­занный в ней файл. Такая проверка не производится и позднее, когда объявля­ется внешняя процедура для функции этой библиотеки. Поэтому, если путь содержит ошибку, вы узнаете об этом только при первом выполнении функции.

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

Ниже подробно описан процесс создания подпрограммы PL/SQL, которая бу­дет служить оболочкой библиотечной функции, вызываемой из Oracle.

Вызов внешней функции

Вызов функции может быть задан в процедуре или функции PL/SQL верхнего уровня, процедуре или функции пакета либо в методе объекта. Более того, специ­фикация вызова может определяться как в спецификации, так и в теле пакета, или же в спецификации либо в теле объектного типа. Приведем несколько при­меров.

CREATE FUNCTION имя {аргументы) RETURN тип_возврвщдемых_дднных AS спецификация_вызовд;

Вы, вероятно, узнали в первом примере функцию shell О, описанную ранее в этой главе. Можно создать и процедуру:

CREATE PROCEDURE имя AS спецификация^вызова:

В данном случае соответствующая функция С должна быть объявлена как void.

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

Параметр AGENT 'связь_базы_данных_агента' в инструкции CREATE LIBRARY — это не­обязательный параметр, задающий соединение с базой данных (в версии Oracle9i и выше), к которой имеет доступ владелец библиотеки. Это соединение должно быть ассоциировано с именем сервиса для внешней процедуры. Предложение AGENT позволяет внешней процедуре выполняться на другом сервере базы данных, хотя физически она может располагаться на том же компьютере.

Пользуясь инструкцией CREATE LIBRARY, имейте ввиду следующее:

- инструкция должна выполняться администратором базы данных либо пользо­вателем с правами CREATE LIBRARY или CREATE ANY LIBRARY;

- как большинство других объектов базы данных, библиотека принадлежит кон­кретному пользователю (схеме) Oracle, который автоматически получает право на ее выполнение и может предоставлять и отменять такое право для других пользователей;

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

- выполняя инструкцию CREATE LIBRARY, Oracle не проверяет, существует ли ука­занный в ней файл. Такая проверка не производится и позднее, когда объявля­ется внешняя процедура для функции этой библиотеки. Поэтому, если путь содержит ошибку, вы узнаете об этом только при первом выполнении функции.

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

Ниже подробно описан процесс создания подпрограммы PL/SQL, которая бу­дет служить оболочкой библиотечной функции, вызываемой из Oracle.

ДЕ19: Внешние процедуры

Вызов функции может быть задан в процедуре или функции PL/SQL верхнего уровня, процедуре или функции пакета либо в методе объекта. Более того, специ­фикация вызова может определяться как в спецификации, так и в теле пакета, или же в спецификации либо в теле объектного типа. Приведем несколько при­меров.

CREATE FUNCTION имя {аргументы) RETURN тип_возврвщдемых_дднных AS спецификация_вызовд;

Вы, вероятно, узнали в первом примере функцию shell О, описанную ранее в этой главе. Можно создать и процедуру:

CREATE PROCEDURE имя AS спецификация^вызова:

В данном случае соответствующая функция С должна быть объявлена как void.

Далее представлена пакетная функция, для которой не требуется создавать тело пакета:

CREATE PACKAGE имя_пакета AS

FUNCTION имя RETURN тип_возвращдемых_данных AS спецификдЦИЯ_вызовд; END;

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

CREATE PACKAGE имяjiaкета AS

PROCEDURE имя:

END;

CREATE PACKAGE BODY имя_пакета AS

PROCEDURE имя; IS спецификация^вызова; END;

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

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

- предоставить читателю информацию, необходимую для загрузки классов Java в базу данных Oracle и управления новыми объектами базы данных, а также для их объявления с целью дальнейшего использования в PL/SQL;

- изложить основные требования и рекомендации по разработке классов Java, достаточные для создания собственных классов, без которых невозможен дос­туп к функциональным возможностям, предоставляемым Java.

Для того чтобы вызвать методы классов Java из Oracle, необходимо выпол­нить действия, перечисленные ниже.

  1. Создать элементы кода Java в Oracle JDeveloper или любой другой интегриро­ванной среде разработки приложений Java. (Более того, это можно сделать в лю­бом текстовом редакторе!)

  2. Загрузить классы Java в Oracle с помощью утилиты командной строки load-Java или инструкции CREATE JAVA.

  3. Предоставить доступ к методам классов Java в PL/SQL путем написания на PL/SQL программ-оболочек для Java-кода.

  4. Назначить необходимые атрибуты доступа программам-оболочкам PL/SQL и использующимся в них Java-классам.

В результате полученные программы PL/SQL можно будет вызывать из са­мых разных сред. Для работы с Java программистам предоставляется ряд средств.

Подготовка к использованию Java в Oracle

Прежде чем вызывать методы Java из программ PL/SQL, нужно выполнить дей­ствия, перечисленные ниже:

- установить пакет Java Development Kit (JDK™), предварительно ознакомив­шись с последней информацией о поддержке версий, которая содержится в документации по Oracle;

- определить классы и элементы кода Java, а затем откомпилировать их, создав файлы .class и jar;

- задать привилегии для схемы Oracle.

Установка Java

Вы можете установить пакет Java Development Kit (J"DK) версии 1.1.5 или выше, загрузив его с web-узла Javasoft:

http://www.j avasoft.com/products/i ndex.html

Начиная с Oracle8i этот пакет входит и в состав Oracle. Обязательно задайте переменную окружения CLASSPATH, и компилятор Java (javac) сможет найти ссылки на ваши классы и на классы Oracle. Дополнительная информация о переменной CLASSPATH содержится в документации по языку Java, которую можно получить, обратившись по адресу http://java.sun.com.

Проектирование и компиляция кода Java

Для многих программистов, не обладающих навыками работы с объектно-ориен­тированными языками, переход к Java может оказаться далеко не простым делом. Поэтому очень уместными здесь будут следующие выводы, основанные на лич­ном опыте автора настоящей главы:

- освоение базового синтаксиса языка, достаточного для создания простых клас­сов Java, требует сравнительно немного времени;

- использование Java в PL/SQL не сопряжено с преодолением каких-то особых трудностей;

- создание объектно-ориентированных приложений на базе Java требует от раз­работчика PL/SQL полного переосмысления привычных подходов.

Ознакомившись с этой главой, вы поймете, как вызываются методы Java из PL/SQL, и научитесь создавать простейшие классы Java.

Определение привилегий, необходимых для разработки и выполнения Java-кода

Поскольку функции системы безопасности, связанные с разработкой и выполне­нием Java-кода, в Oracle8f и Oracle9i реализованы по-разному, мы рассмотрим ка­ждую из версий.

Защита данных в Oracle

В РСУБД Oracle8i определены две роли, предоставляющие привилегии на вы­полнение разных видов операций:

О JAVAUSERPRIV — привилегии на выполнение относительно небольшого количе­ства операций, в том числе на просмотр свойств;

О JAVASYSPRIV — привилегии на выполнение большей части операций, включая обновление пакетов, защищенных JVM.

Эти роли назначаются таким же образом, как любые другие роли базы данных. Для того чтобы пользователю Scott получить, скажем, разрешение на любые дей­ствий относительно Java, ему нужно подключиться к Oracle с учетной записью SYSDBA и выполнить такую инструкцию:

GRANT JAVASYSPRIV TO SCOTT;

При необходимости предоставить этому же пользователю ограниченные пра­ва на работу в Java, следует ввести инструкцию

GRANT JAVAUSERPRIV TO SCOTT;

Например, чтобы получить возможность создать файл с помощью Java, нужно быть членом роли JAVASYSPRIV, а для чтения или записи содержимого этого файла достаточно быть членом роли JAVAUSERPRIV. За дополнительными сведениями по

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

В процессе инициализации JVM Aurora устанавливает экземпляр менеджера защиты Java Gava Security Manager) — Java. 1 ang. Securi tyManager. Каждый пользо­ватель Oracle имеет динамический идентификатор, определяющий права, или при­вилегии, владельца сеанса при доступе к методам Java из PL/SQL.

Если пользователь, не имеющий достаточных полномочий, пытается выпол­нить запрещенную операцию, JVM инициирует исключение Java. I ang. Securi-tyException. Вот что увидит пользователь в таком случае в SQL*Plus:

ORA-29532: Java call terminated by uncaught Java exception: Java.1ang.SecurityException

При вызове методов Java в базе данных возможно возникновение различных проблем, связанных с системой безопасности, особенно при взаимодействии с сер­верной файловой системой или другими ресурсами операционной системы. Кон­тролируя операции ввода-вывода, Oracle следует определенным правилам.

Если пользователю, имеющему персональный идентификатор прав доступа, были предоставлены привилегии JAVASYSPRIV, Security Manager разрешает вы­полнить операцию.

Если же такому пользователю были предоставлены привилегии JAVAUSERPRIV, Security Manager проверяет допустимость операции в соответствии с теми пра­вилами, которые действуют в отношении пакета PL/SQL UTL^FILE. Иными сло­вами, файл должен находиться в каталоге, определяемом параметром UTL_FI-LE_DIR, который задается в инициализационном файле базы данных.

Защита данных в Oracle9i

Система защиты, реализованная в Oracle9i для JVM, основана на модели защиты Java 2, согласно которой привилегии предоставляются каждому из существую­щих классов. Подробный анализ этой сложной системы выходит за рамки данной книги. Однако мы приведем несколько примеров, чтобы вы могли составить пред­ставление о программном коде, который можно писать в Oracle9* для предостав­ления и отмены привилегий на выполнение определенных действий над объекта­ми базы данных.

В общем случае для предоставления привилегий используется процедура DBMS_ JAVA.GRANT_PERMISSION. Пример ее вызова для предоставления схеме BATCH разреше­ния на чтение и запись файла 1astorder.log выглядит следующим образом:

CALL DBMS_JAVA.GRANT_PERMISSION( 'BATCH'.

'Java.io.FilePermission'. Vapps/ОЕЛastorder.log'. 'read.write');

Приведем последовательность операторов, которая сначала предоставляет раз­решения на доступ к файлам в каталоге, а затем ограничивает их возможностью выполнения операций над заданным файлом:

CONNECT OE_admin/OE_admin

REM Grant permission to all users (PUBLIC) to be REM able to read and write all files in /tmp.

Во времена, когда программисты работали с Огас1е7, они часто задавали вопрос: «Какие средства можно вызывать из Oracle?» Как правило, под средствами под­разумевалась система электронной почты, команды операционной системы или функции, реализованные в других языках программирования. И хотя после вклю­чения в состав Oracle пакета UTLSMTP проблема электронной почты потеряла свою актуальность, методов выполнения других внешних по отношению к Oracle опе­раций по-прежнему существует не очень много. Для реализации внешних опера­ций можно сделать следующее:

О написать программу в виде хранимой процедуры Java и затем вызывать ее из кода PL/SQL;

О использовать таблицу базы данных или очередь как буфер для записи запро­сов и создать отдельный процесс для их считывания и обработки;

О создать канал базы данных и написать демон, отвечающий на входящие запросы; О написать программу на языке С и вызывать ее как внешнюю процедуру.

Кратко рассмотрим каждый из этих методов. Код Java подходит в том случае, если его быстродействия достаточно для вашего приложения. Интересна техно­логия, базирующаяся на использовании очереди, но даже применение обыкно­венных таблиц требует запуска двух сеансов Oracle: одного для записи запросов в очередь, а другого — для считывания. Более того, для каждого сеанса выделяется отдельное пространство транзакций, что может создать дополнительные сложно­сти при работе приложения. Применение канала базы данных связано не только с проблемой двух сеансов, но и с упаковкой и распаковкой данных, передаваемых

по каналу. Кроме того, необходимость одновременной обработки множества за­просов с использованием любого из этих подходов может потребовать от вас соз­дания собственного процесса прослушивания соединения — листенера (listener) — и системы диспетчеризации процессов.

Учитывая все это, мы остановились на четвертом методе. Применение внеш­них процедур позволяет выполнять в PL/SQL практически любые задачи, реали­зуемые средствами других языков программирования, и в то же время избежать проблем, характерных для остальных упомянутых подходов. У программистов PL/SQL обычно возникает множество вопросов относительно внешних проце­дур: как они работают, насколько защищены, каким образом проектировать их са­мостоятельно, каковы их достоинства и недостатки. В настоящей главе мы не только попытаемся дать ответы на все эти вопросы, но и приведем примеры, де­монстрирующие большинство возможностей внешних процедур.

Концепция внешних процедур

Для того чтобы внешнюю программу можно было вызвать из среды Oracle, ее не­обходимо реализовать в виде библиотеки, доступной для совместного использо­вания. Речь идет о библиотеках динамической компоновки (DLL), используемых Microsoft, и общих библиотеках системы UNIX, хранящихся в файлах с расшире­нием .so (shared object). Теоретически внешнюю процедуру можно написать на любом языке, но компилятор и компоновщик должны сгенерировать библиотеку, которую можно вызывать из кода, написанного на языке С. Чтобы обеспечить возможность вызова внешней программы из PL/SQL, для нее нужно написать специальную оболочку PL/SQL, называемую спецификацией вызова. Если внеш­няя программа возвращает значение, ей ставится в соответствие функция PL/SQL, а если она ничего не возвращает — процедура PL/SQL.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]