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

VI Способы взаимодействия с Oracle из языков программирования.

ДЕ22: Технологии доступа

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

конфигурироваться сервер с учетом защиты, как написать внешнюю

процедуру с помощью прекомпилятора Oracle Pro*C. Эта внешняя процедура будет использоваться для записи содержимого любого большого объекта в файловую систему сервера. Однако прежде чем перейти к этому примеру, рассмотрим базовый пример, демонстрирующий, как передавать значения основных типов данных из языка PL/SQL в функции на языке С и получать результаты. Этот базовый пример позволит также создать шаблон для быстрой разработки всех последующих внешних процедур на языке С. Вы узнаете, как создавать код на языке С с учетом возможности использования его для выполнения сервером Oracle. Мы также рассмотрим реализацию SQL-оболочки для внешней процедуры. Я расскажу, как обеспечить возможность ее вызова из языков SQL и PL/SQL и как программировать оболочку, чтобы ее легко было использовать тем, кому нужен соответствующий код. Наконец, мы рассмотрим преимущества и недостатки внешних процедур, а также различные ошибки сервера (ошибки ORA-XXXX), которые могут возникнуть при их использовании.

В примере на языке Рго*С будет создаваться недостающее средство сервера. В составе сервера Oracle поставляется пакет DBMS_LOB для работы с большими объектами. В этом пакете есть процедура loadfromfile, позволяющая читать в большой объект базы данных содержимое любого файла в файловой системе сервера. Однако в этом пакете нет функции writetofile, которая могла бы записывать в файловую систему содержимое большого объекта, а такая потребность часто возникает. Мы решим эту задачу, создав собственный пакет LOB_IO. Этот пакет позволит записывать любой большой объект типа BLOB, CLOB или BFILE в отдельный файл вне базы данных (так что для объектов типа

BFILE мы по сути создадим команду копирования, поскольку исходный объект BFILE уже находится вне базы данных).

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

жизни. У каждого языка есть недостатки: не все можно сделать с его помощью, недостает возможностей или средств, о которых разработчики не подумали. При разработке программ на языке С мне иногда приходится программировать на ассемблере. При программировании приложений для Oracle на Java иногда удобно использовать код на языке PL/SQL. Суть в том, что не нужно всегда использовать язык "низкого уровня" — иногда необходимо переходить на более высокий уровень. Внешние процедуры можно считать переходом на более "низкоуровневый" язык. Они обычно используются для интеграции существующего кода на языке С в виде библиотек функций (например, DLL — динамически компонуемых библиотек в Windows, созданных сторонним производителем, которые необходимо вызывать с сервера) или для расширения функциональных возможностей существующих пакетов, как в нашем случае. Именно эту технологию используют разработчики сервера Oracle для расширения его возможностей.

Существует возможность стороннего сетевого взаимодействия "научив" язык PL/SQL использовать сокеты, открывается широкий спектр новых возможностей:

• посылать сообщение электронной почты из триггера с помощью протокола SMTP;

• включать сообщения электронной почты в базу данных с помощью протокола

POP;

• индексировать сообщения дискуссионных групп с помощью компонента interMedia Text и протокола NNTP;

• обращаться к любой доступной сетевой службе.

Начиная с версии 8.1.6 сервера Oracle, все возможности, которые обеспечивал простой клиент TCP/IP, теперь реализуются в пакете UTL_TCP.__

Как реализована поддержка внешних процедур?

Внешние процедуры выполняются процессом, физически отделенным от процессов сервера. Это сделано из соображений защиты. Хотя технически можно динамически загружать DLL-библиотеку (в Windows) или файл .so (Shared Object code — разделяемый объектный код, скажем, в Solaris) во время выполнения и в существующих процессах сервера, при этом сервер будет подвергаться неоправданному риску. Код библиотеки будет иметь доступ к тому же пространству памяти, что и процессы сервера, в том числе и к системной глобальной области Oracle (SGA). В результате этот посторонний код может случайно повредить базовые структуры данных СУБД, что приведет к потере

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

В большинстве случаев отдельный процесс будет работать от имени пользователя, не являющегося владельцем программного обеспечения Oracle. Причина та же, что и в случае выполнения внешних процедур отдельным процессом — безопасность. Пусть, например, мы собираемся создать внешнюю процедуру, которая сможет записывать файлы на диск (как это делает процедура, рассматриваемая далее). Предположим, сервер работает в среде UNIX, и внешняя процедура выполняется от имени владельца программного обеспечения Oracle. Пользователь вызывает новую функцию и "просит" ее записать объект BLOB в файл /d0l/oracle/data/system.dbf. Поскольку этот код выполняется от имени пользователя — владельца программного обеспечения Oracle, этот вызов сработает и непреднамеренно запишет содержимое какого-то большого двоичного объекта вместо системного табличного пространства. Мы можем этого даже и не заметить до момента остановки и перезапуска сервера (много дней спустя). Если бы внешняя процедура выполнялась от имени менее привилегированного пользователя, это не могло бы случиться (этот пользователь не имел бы права на запись файла system.dbf) Поэтому в разделе, посвященном конфигурированию сервера для поддержки внешних процедур, мы рассмотрим, как настроить безопасный процесс прослушивания EXTPROC (EXTemal

PROCedure), работающий от имени другой учетной записи ОС. Причины такой настройки примерно те же, что и в случае запуска Web-серверов от имени пользователя nobody в UNIX или от имени учетной записи с минимальными привилегиями в Windows.

Код на языке С

Теперь можно переходить к реализации библиотеки на языке С. Начнем с общего

шаблона, который используется для внешних процедур. Этот шаблон включает стандартные заголовочные файлы, заголовочный файл Oracle OCI и три функции: debugf, oci_error и raise_application_error. Эти функции обеспечивают поддержку механизма трассировки (debugf) и общей обработки ошибок (oci_error и raise_application_error). Просто копируется этот файл в каждый новый проект создания внешних процедур и начинаю разработку с него.

Выше представлено начало используемого шаблона на языке С. Включаются

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

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#include <string.h>

#include <errno.h>

#include <ctype.h>

#include <oci.h>

#ifdef WIN_NT

#define INI_FILE_NAME "c:\\WtempWextproc.ini"

#else

#define INI_FILE_NAME "/tmp/extproc.ini"

#endif

#define strupr(a) {char * cp; for(cp=a;*cp;*cp=toupper(*cp), cp++);}

Windows, можно было бы использовать функции Windows RegOpenKeyEx, RegQueryInfoKey и RegEnumValue для получения сведений о местонахождении файла параметров из системного реестра. В среде UNIX можно использовать переменную среды. В этом примере я просто явно указываю местонахождение в коде программы. Это вполне допустимо, поскольку можно потребовать, чтобы параметры инициализации помещались в известный стандартный файл (например, в файл /еtс/имя_внешней_процедуры.ога в ОС UNIX или

с:\имя_внешней_процедуры\имя_внешней_процедуры.ога в Windows).

Теперь переходим к самому коду. В следующей части определяется контекст. Он

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

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

Создание внешней процедуры

Давайте сначала рассмотрим универсальный make-файл для Windows:

*return_i = OCI_IND_NOTNULL;

}

term(myCtx);

return return_value;

}

CPU=i386

MSDEV = c:\msdev (1)

ORACLE_HOME = c:\oracle (2)

!include <$(MSDEV)\include\win32.mak> (3)

TGTDLL = extproc.dll (4)

OBJS = extproc.obj (5)

NTUSER32LIBS = $(MSDEV)\lib\user32.1ib \ (6)

$(MSDEV)\lib\msvcrt.lib \

$(MSDEV)\lib\oldnames.lib \

$(MSDEV)\lib\kernel32.1ib \

$(MSDEV)\lib\advapi32.lib

SQLLIB = $(ORACLE_HOME)\precomp\lib\msvc\orasql8.1ib \ (7)

$(ORACLE_HOME)\oci\lib\msvc\oci.lib

INCLS = -I$(MSDEV)\include \ (8)

-I$(ORACLE_HOME)\oci\include \

-I.

CFLAGS = $(INCLS) -DWIN32 -DWIN_NT -D_DLL (9)

1330 Глава 18

Внешние процедуры на языке С

a l l : $(TGTDLL) (10)

clean: (11)

erase *.obj * . l i b *.exp

$(TGTDLL): $(OBJS) (12)

$(link) -DLL $(dllflags) \

/NODEFAULTLIB:LIBC.LIB -out:$(TGTDLL) \

$(0BJS) \

$(NTUSER32LIBS) \

$(SQLLIB)

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

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

1. Это каталог, в котором у меня установлен компилятор языка С. Я использую компилятор Microsoft Visual C/C++, который поддерживается в среде Windows. Значение этой переменной я использую позже в make-файле, когда необходимо сослаться на этот каталог.

2. Мой каталог ORACLE_HOME. Он используется при поиске включаемых файлов для ОСI/Pro*С и стандартных библиотек Oracle.

3. Я включаю стандартный шаблон make-файла Microsoft. Он задает переменные $(link) и $(dllflags), которые могут быть разными для различных версий компилятора.

4. Переменная TGTDLL задает имя создаваемой DLL-библиотеки.

5. Переменная OBJS задает список объектных файлов, используемых при создании библиотеки. Если распределить код по нескольким файлам, в списке будет указано несколько объектных файлов. В нашем несложном примере используется только один объектный файл.

6. Переменная NTUSER32LIBS содержит список стандартных системных библиотек, с которыми выполняется компоновка.

7. Переменная SQLLIB содержит список необходимых библиотек Oracle. В данном примере я компоную библиотеки как Pro*С, так и OCI, хотя используются только библиотеки OCI. Но от включения библиотек Pro*С вреда не будет.

8. Переменная INCLS содержит список каталогов, в которых находятся необходимые включаемые файлы. В данном случае мне необходимы системные заголовочные файлы, а также заголовочные файлы сервера Oracle и текущий рабочий каталог.

9. Переменная CFLAGS — стандартный макрос языка С, используемый компилятором. Я определяю макрос -DWIN_NT, для условной компиляции кода, предназначенного для NT (например, _declspec(dllexport)).

10. Цель all: по умолчанию будет создавать DLL-библиотеку.

11. Цель clean: требует удалить временные файлы, созданные в ходе компиляции.

12. Цель TGTDLL требует выполнить команду, создающую DLL-библиотеку. Она скомпилирует и скомпонует весь необходимый код.

Резюме

Мы рассмотрели тонкости использования внешних процедур:

• поддержка информации о состоянии с помощью контекстов;

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

• параметризация кода внешней процедуры с помощью внешних файлов парамет-

ров;

• оснащение кода средствами отладки (с помощью макроса debugf), позволяющи-

ми найти причину проблем;

• приемы написания безопасного кода (всегда передавать контекст, всегда переда-

вать индикаторы значений NULL и т.д.);

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

цедур с широкими функциональными возможностями;

• различия между внешними процедурами, использующими исключительно библиотеку OCI, и процедурами, использующими средства прекомпилятора Рго*С;

• как сопоставлять, принимать и передавать основные типы данных PL/SQL во внешние функции на языке С;

• как передавать и принимать наборы данных.

Имея представленные выше универсальный шаблон и файлы управления проектом, вы получили все необходимое для написания внешней процедуры от начала до конца за несколько минут. Самое сложное — сопоставление типов данных, но по представленным в этой главе таблицам это легко сделать. Они вам подскажут, какой тип использовать для передачи данных. Далее следуйте представленным в примерах принципам передачи параметров (всегда передавать контекст, всегда передавать атрибут MAXLEN для строк и данных типа RAW, всегда передавать индикаторные переменные и т.д.). Это обеспечит создание эффективных внешних процедур в кратчайшие строки.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]