ОГЛАВЛЕНИЕ
ВВЕДЕНИЕ. ЧТО ТАКОЕ СЕРВЕР ПРИЛОЖЕНИЙ "СМАРАГД"?
1. ОБЗОР СЕРВЕРА ПРИЛОЖЕНИЙ "СМАРАГД"
2. ИНТЕРФЕЙС СЕРВЕРА ПРИЛОЖЕНИЙ
3. ХРАНИМЫЕ ПРОЦЕДУРЫ СЕРВЕРА ПРИЛОЖЕНИЙ
4. ЯЗЫК ХРАНИМЫХ ПРОЦЕДУР СЕРВЕРА ПРИЛОЖЕНИЙ
Особенности взаимодействия КЛИЕНТОВ с сервером приложений в FoxPro 2.6.
ПРИМЕР ИСПОЛЬЗОВАНИЯ СЕРВЕРА ПРИЛОЖЕНИЙ
ВВЕДЕНИЕ. ЧТО ТАКОЕ СЕРВЕР ПРИЛОЖЕНИЙ "СМАРАГД"?
С
ервер приложений (СП) "СМАРАГД" - это набор программного обеспечения, позволяющий распределить обработку данных по сети, организовать специально выделенные серверы для выполнения определенных задач, многозадачный режим выполнения программ пользователя *).ПРЕИМУЩЕСТВА ТРЕХЗВЕННОЙ АРХИТЕКТУРЫ
В
традиционных архитектурах клиент/сервер (двухзвенных архитектурах) взаимодействие клиентской программы и сервера баз данных происходит напрямую. При этом вся логика обработки данных делится между клиентскими программами и серверами баз данных. На серверах баз данных производится первичная обработка данных с помощью механизма хранимых процедур, а их вторичная обработка данных выполняется на клиентском рабочем месте. При этом подходе при изменении структуры базы данных, сервера базы данных, порядка выполнения определенных операций над данными необходимо менять либо хранимые процедуры сервера, либо программы клиента. Первый вариант более предпочтителен, но все равно, для изменения процедуры, которой активно пользуются клиенты, необходимо произвести отключение пользователей от сервера. Одним из основных недостатков этого подхода является отсутствие возможности абстрагирования клиента от терминологии СУБД, от понятия СУБД, от конкретных серверов баз данных. Другим недостатком такого подхода является сильная нагрузка на клиентские программы из-за необходимости дополнительной обработки данных совместно с управлением интерфейсом с пользователем. Также при использовании двухзвенных архитектур возрастает "бесполезная" нагрузка на сеть, поскольку решение о том, нужны данные или нет, может быть принято при вторичной обработке на клиенте.При использовании трехзвенных архитектур появляется возможность снять часть нагрузки с клиента и сервера баз данных на специально выделенный сервер приложений. В этом случае появляется возможность проводить вторичную обработку данных отдельно от обработки интерфейса с пользователем и передавать только актуальные данные от сервера приложений к клиенту. При изменении алгоритма обработки необходимо менять некоторые модули на сервере приложений, а не все клиентские программы. При использовании сервера приложений можно организовать общение клиента с СП в абстрактных терминах, а не в терминах СУБД.
Таким образом, при использовании сервера приложений можно решить ряд проблем, возникающих перед разработчиками традиционных двухзвенных систем.
ВОЗМОЖНОСТИ СЕРВЕРА ПРИЛОЖЕНИЙ "СМАРАГД"
С
ервер приложений "СМАРАГД" обладает следующими возможностями:1. ОБЗОР СЕРВЕРА ПРИЛОЖЕНИЙ "СМАРАГД"
В
данной главе приводится общий обзор архитектуры сервера приложений "Смарагд", варианты его использования, полное описание схемы работы сервера, требования к пользовательским процессам и краткое описание интерпретатора "Смарагд".С
ервер приложений "Смарагд" может поставляться в одном из двух вариантов: базовом и расширенном. В базовом варианте поставляется только ядро сервера приложений (описание ядра сервера приложений см. в разделе "Ядро сервера приложений"). В расширенном варианте вместе с ядром сервера поставляется внешний интерпретатор языка ASPL (описание языка ASPL см. в главе "Язык хранимых процедур").Общая схема сервера приложений показана на рис. 1.
Рис. 1. Схема сервера приложений
Работа клиента с сервером приложений идет на уровне сессий. Сессия - это функционирующий процесс пользователя, запущенный ядром и обменивающийся данными через собственный специальный канал обмена. Канал обмена предназначен для обеспечения возможности отключения пользователем от текущей сессии и подключения к ней через некоторое время. Если за время, когда пользователь был отключен от канала, процессом клиента были выданы некоторые данные, они заносятся в канал и в дальнейшем, при подключении пользователя, тот считывает сохраненные данные из канала.
Для управления работой сессий и ядра, а также для выполнения определенных административных операций существует специальный набор команд для ядра сервера приложений. Подробнее об этих командах см. в разделе "Интерфейс сервера приложений".
Общая схема взаимодействия клиента и сервера приложений следующая.
Клиент открывает соединение с ядром сервера приложений и дает команду на начало новой сессии. По этой команде ядро запускает процесс, имя которого передано клиентом, или процесс по умолчанию, который записан в файле конфигурации. Далее, клиент и клиентский процесс обмениваются некоторыми данными. После того как клиент решил завершить сессию, он посылает команду ядру на завершение процесса клиента. При получении этой команды ядро сервера приложений останавливает процесс клиента (если тот не был остановлен самостоятельно), уничтожает канал клиента и выдает подтверждение завершения сессии. Связь с клиентом при этом не разрывается.
В другом варианте работы после запуска сессии и первичного обмена данными клиент может подать команду на отключение от сессии. После этого клиент отключается от канала запущенного процесса и может порождать новую сессию или отключиться от сервера. При этом клиентский процесс будет продолжать исполнение. Через некоторое время подключенный к ядру, но отключенный от всех сессий клиент дает ядру команду на подключение к работающей сессии, после проверки полномочий он подключается к каналу сессии и получает результаты работы процесса клиента.
Ядро сервера приложений представляет собой непосредственно сервер приложений и набор административных утилит для управления им. Функциями ядра являются задачи управления сессиями пользователя, сбор и предоставление информации о состоянии процессов пользователя администратору. Как описывалось выше, к задачам по управлению сессиями относятся задачи создания новой сессии, подключения к текущей сессии, отключения от нее, завершения текущей (или указанной администратором) сессии, управление аутентификацией пользователей, изменение приоритетов сессий.
Клиентский процесс сервера приложений - это, в общем виде, произвольный исполняемый файл, работающий через стандартный ввод/вывод. В расширенном варианте поставки сервера существует интерпретатор языка
ASPL, который может использоваться как клиентский процесс.На формат выходных данных клиентского процесса накладывается (необязательное) ограничение, при несоблюдении которого клиенты не могут воспользоваться стандартной библиотекой взаимодействия клиента с сервером приложений, поэтому им необходимо использовать собственные библиотеки.
1.2 ОБРАБОТКА ЗАПРОСОВ КЛИЕНТОВ
О
бработка запросов клиентов производится в многопоточном режиме на уровне ядра и в многозадачном режиме на уровне клиентских процессов. Для каждой сессии запускается собственный процесс, таким образом, клиент может использовать возможности вытесняющей многозадачности даже, если он запущен в однозадачной операционной системе.На каждую команду клиента ядро сервера приложений высылает или подтверждение, или опровержение выполнения команды. Форматы этих ответов приведены в разд. "Интерфейс сервера приложений".
1.3 УПРАВЛЕНИЕ РАБОТОЙ СЕРВЕРА
У
правление работой сервера осуществляется путем посылки специальных команд ядру севера. Эти команды являются привилегированными, и посылать их может только администратор сервера приложений.Замечание. Администратор операционной системы (
root в UNIX или Administrator в Windows NT) не имеет права исполнять эти команды, если он не является администратором сервера приложений. В случае посылки им этих команд, сервер приложений вернет ошибку.1.4 ЖУРНАЛИЗАЦИЯ РАБОТЫ СЕРВЕРА
В
процессе своей работы сервер ведет журнал работы, в который он записывает информацию о действиях клиентов. Этот журнал записывается в файл. Имя файла журнала указывается в файле конфигурации сервера.2. ИНТЕРФЕЙС СЕРВЕРА ПРИЛОЖЕНИЙ
В
заимодействие клиента и сервера приложений базируется на протоколе TCP/IP и организуется в виде обмена текстовыми строками (пакетами). Интерфейс клиента с сервером приложений является двухуровневым:Для каждой команды клиента сервер приложений возвращает подтверждение о ее завершении, что является признаком окончания выполнения команды, или сообщение об ошибке. Сервер приложений поддерживает асинхронное выполнение команд клиента.
В общем виде структура клиентского пакета может быть записана в виде:
Рис. 2
. Общий формат пакета клиента.Сигнатура пакета - стандартный 4-байтовый неизменяемый префикс пакета ("ASIF").
Длина пакета - беззнаковое длинное целое, показывающее размер тела пакета в байтах.
Тип пакета - однобайтовый символ, который принимает значение 'C' или 'D'.
Тело пакета - произвольная строка, содержащая команду или данные для сервера приложений (в общем виде - поток байт).
Пакеты клиента делятся на 2 вида:
ЯЗЫК КОМАНД СЕРВЕРА ПРИЛОЖЕНИЙ
Элементы языка - это команды, которые делятся на следующие классы:
LI
<имя_клиента> <пароль>BS
или
BS
<имя запускаемого файла>AS <
идентификатор_сессии>Рассмотрим примеры пакетов клиентов с использованием выше- перечисленных команд:
ASIF13CLI user
1 pwd1 /* для команды LI, гдеuser1 - имя клиента,
pwd1 - пароль */
ASIF2CBS или ASIF12CBS file1.exe /* для команды BS, где
file1.exe - имя файла */
ASIF2CES /* для команды ES */
ASIF2CDS /* для команды DS */
ASIF
7CAS id_s /* для команды AS, гдеid_s - идентификатор сессии*/
ASIF2CLU /* для команды LU */
ASIF2CLT /* для команды LT */
В общем виде структура ответного пакета сервера приложений может быть записана следующим образом:
Рис. 3
. Общий формат ответного пакета сервера приложенийСигнатура ответного пакета - стандартный 4-байтовый неизменяемый префикс пакета ("ASIF").
Длина ответного пакета - беззнаковое длинное целое, показывающее размер тела ответного пакета в байтах.
Тип ответного пакета - однобайтовый символ, который принимает одно из следующих значений: 'D', 'R' или 'E'.
Тело ответного пакета - произвольная строка, содержащая ответ сервера приложений.
Данные, получаемые в ответном пакете, представляют собой неформатированные ("сырые") данные
.На к
аждую команду клиента сервер приложений высылает подтверждение о ее выполнении или сообщение об ошибке в случае невыполнения команды. Формат ответного пакета следующий:LU <
:
LU END
LT
<описание задачи>:
LT END
Рассмотрим примеры ответных пакетов для указанных выше команд:
ASIF8RLI user1 /* для команды LI, где
user1 - имя клиента */
ASIF2RBS /* для команды BS */
ASIF2RES /* для команды ES */
ASIF7RDS id_s /* для команды DS, где
id_s -
идентификатор сессии */ASIF2RAS /* для команды AS */
ASIF8RLU BEGIN /* для команды LU, где
BEGIN указывает на начало описания
клиента */
ASIF12RLU user1 1 2 /* user1 - имя клиента,
1 - номер сессии,
2 - номер устройства, куда
подключен клиент */
ASIF12RLU user2 2 3
ASIF6RLU END /* END указывает, что описание
закончено */
ASIF8RLT BEGIN /* для команды LT, где
BEGIN указывает на начало описания
процесса */
ASIF14RLT 1 use
r1 1 2 /* 1 - номер процесса,user1 - имя владельца процесса,
1 - номер сессии,
2 - номер устройства, на котором
запущен процесс */
ASIF15RLT 2 user2 3 -1 /* -1 - процесс не запущен */
ASIF6RLT END /* END указывает, что описание
процесса закончено */
Пакеты клиента передаются и принимаются с сервера приложений с помощью набора функций, обеспечивающих взаимодействие клиента с сервером приложений.
Особенности использования функций взаимодействия с сервером приложений в
FoxPro 2.6 описаны в приложении 2.В
Delphi и Borland C++ Builder (BCB) создан специальный класс Tconnect, методы (Methods) и свойства (Properties) которого обеспечивают взаимодействие с сервером приложений.Назначение: устанавливает соединение с сервером приложений с помощью библиотеки сокетных функций.
Синтаксис:
CONNECT* ConnectTo (char* hostname, char* service);
CONNECTTO(hostname, port)
Вызов функции:
CALL gl_connectto (hostname, hlen, services, slen) RETURNING index)
Входные значения
:hostname - имя компьютера, где расположен сервер приложений;
service - наименование сервиса;
port -
номер порта;hlen -
длина переменной 'hostname';slen -
длина переменной 'service'.Возвращаемые значения
: Переменная типа CONNECT, в которой хранятся параметры установленного соединения.
Целое число в интервале 0ё 31, служащее идентификатором установленного соединения.
Чтобы установить соединение с сервером, необходимо:
Назначение: закрывает соединение с сервером приложений.
Синтаксис:
int CloseConnect (CONNECT* con);
CLOSECONN(conid)
Вызов функции
:CALL gl_closeconnect (index) RETURNING sc
Входные значения:
con - переменная типа CONNECT с параметрами соединения;
conid - идентификатор соединения;
index -
идентификатор соединения.
Возвращаемые значения:
0 в случае успешного завершения, -1 в случае ошибки.
Чтобы закрыть соединение с сервером приложений необходимо свойству Connected присвоить значение FALSE.
Назначение
: посылает командные пакеты клиента на сервер приложений.Синтаксис:
int SendCmd (CONNECT* con, char* command);
SENDCMD(conid, command)
Function SendCmd(Cmd: String):Boolean;
bool __fastcall SendCmd(System::AnsiString Cmd1);
Вызов функции
:CALL gl_sendcmd (index, cmd, len) RETURNING sc
Входные значения
:con -
переменная соединения типа CONNECT;conid - идентификатор соединения;
command -
посылаемая команда;index -
идентификатор соединения;cmd -
строка, содержащая посылаемую команду;len -
длина посылаемой команды. Cmd - посылаемая команда;
Cmd1 - посылаемая команда.
Возвращаемые значения:
TRUE, если функция выполнена успешно, FALSE - в случае ошибки.
Назначение
: посылает пакеты с данными на сервер приложений.Синтаксис:
int SendData (CONNECT* con, char* data);
SENDDATA(conid, data)
Function SendData(Data: String):Boolean;
bool __fastcall SendData(System::AnsiString Data1);
Вызов функции
:CALL gl_senddata (index, data, len) RETURNING sc
Входные значения
:con -
переменная соединения типа CONNECT;conid
- идентификатор соединения;data -
посылаемые данные;index -
идентификатор соединения;len -
длина посылаемых данных.
Data - посылаемые данные;
Data1 - посылаемые данные.
Возвращаемые значения:
Количество переданных байт в случае успешного завершения, -1 в случае ошибки.
TRUE, если функция выполнена успешно, FALSE - в случае ошибки.
Назначение
: ждет ответные пакеты от сервера приложений и принимает их в общем виде вне зависимости от типа пакета.Синтаксис:
char WaitData (CONNECT* con, int TimeOut, char** buf);
WAITDATA(conid, TimeOut, buf)
Вызов функции
:CALL gl_waitdata (index, TimeOut) RETURNING sc, buf
Входные значения
:con -
переменная соединения типа CONNECT;conid - идентификатор соединения;
TimeOut -
время ожидания пакетов;buf -
буфер для присылаемых данных в теле пакета.index -
идентификатор соединения; Параметр buf необходимо передавать по ссылке, т.е. с использованием префикса @.
Возвращаемые значения
:Тип ответного пакета или '\0' в случае отсутствия пакетов.
В момент прихода ответного пакета от сервера приложений, в зависимости от его вида, происходит одно из следующих событий (Event):
OnError, если получено сообщение об ошибке;
OnReply, если получен ответ на команду;
OnData, если получены данные.
Назначение
: разбирает пакеты с данными.Синтаксис:
int RetrieveData (char* buf, VALUE* val);
RETRDATA(buf, val)
Входные значения
:buf -
буфер с общими данными; val - переменная типа VALUE, хранящая значения переменных и констант из ответного пакета типа 'D';
Параметр val необходимо передавать по ссылке, т.е. с использованием префикса @. Функция присваивает данной переменной значение того типа, который указан в ответном пакете.
Возвращаемые значения
:0 в случае успешного завершения, -1 в случае ошибки.
-1 в случае ошибки, 1 - значение пустое, 0 - нет.
Тип
VALUE находится в заголовочном файле sdk.h и описан следующим образом:struct {
int type; //
тип значения, хранящегося в структуреunion {
char c; // символ CHAR(1)
short int si; // SMALLINT
long int li; // INTEGER, INT
float f; // SMALLFLOAT, REAL
double d; // FLOAT, DOUBLE PRECISION
char* p; // CHAR(N), VARCHAR[(N)], CHARACTER(N)
void* vp; // структура для других типов (не используется)
} val;
int null; // NULL-индикатор (1 - значение пустое, 0 - нет)
} VALUE;
В Delphi тип значения хранится в переменной vtype, так как в Object Pascal нельзя использовать в качестве имени переменной служебное слово type.
3. ХРАНИМЫЕ ПРОЦЕДУРЫ СЕРВЕРА ПРИЛОЖЕНИЙ
Х
ранимые процедуры сервера приложений являются основным инструментом, реализующим логику прикладной системы. В рамках трехзвенной архитектуры "клиент-сервер приложений-сервер" такой подход позволяет максимально облегчить создание клиентских приложений и добиться создания так называемого "тонкого клиента". С другой стороны, достигается определенная гибкость в реализации правил обработки данных и их распределении между СУБД, которая может их поддержкой не обладать, и самими хранимыми процедурами сервера приложений. В идеале бизнес-логика приложения реализуется частично на СУБД и частично в процедурах сервера приложений. СУБД следует сделать "ответственной" за целостность данных, максимально используя средства, предоставляемые конкретной реализацией SQL для этой СУБД, а на долю хранимых процедур сервера приложений останутся те функции, которые выходят за пределы возможностей самой СУБД и ее механизмов (триггеров, хранимых процедур БД и т.д.).Следует отличать хранимые процедуры баз данных и хранимые процедуры сервера приложений. Хранимые процедуры баз данных создаются
SQL-оператором CREATE PROCEDURE на языке SPL. Они являются неотъемлемой частью базы данных и хранятся непосредственно в рамках СУБД в пространстве данных конкретной базы данных.Хранимые процедуры сервера приложений создаются в рамках сервера приложений и делятся на два класса:
Далее, если это специально не оговорено, под термином хранимая процедура понимается внутренняя хранимая процедура. Термин внешняя хранимая процедура всегда записывается полностью.
3.1 ВНУТРЕННИЕ ХРАНИМЫЕ ПРОЦЕДУРЫ
В
нутренние хранимые процедуры создаются на языке хранимых процедур сервера приложений ASPL (рассмотрено ниже).Для каждого пользователя сервера приложений существует три класса доступных ему функций и процедур:
Стандартные функции реализованы стандартной библиотекой на
ASPL, хранимые процедуры и функции общего назначения создаются разработчиками (могут быть внешними и внутренними), пользовательские хранимые процедуры (могут быть внешними и внутренними) создаются для каждого пользователя конкретно и могут переопределять хранимые процедуры общего назначения.Подробнее язык хранимых процедур описан в разд.
"Язык хранимых процедур сервера приложений".3.2 ВНЕШНИЕ ХРАНИМЫЕ ПРОЦЕДУРЫ
В
нешние хранимые процедуры сервера приложений внедряются в архитектуру сервера приложений администратором и разделяются на пользовательские процедуры и процедуры общего использования. Они представляют собой отдельные функции динамически подключаемых (разделяемых) библиотек той операционной системы, в которой выполняется сервер приложений.При указании имени процедуры, содержащейся в пользовательской библиотеке и библиотеке общего назначения, преимущество имеет пользовательская библиотека, т.е. будет запущена процедура именно из нее.
Внешняя хранимая процедура сервера приложений может быть создана с использованием любого языка программирования, компилятор которого позволяет создавать разделяемые библиотеки операционной системы. Для
UNIX такие библиотеки, как правило, имеют расширение .so, для Windows .dll.Инсталляция и администрирование пользовательских библиотек
Процесс инсталляции заключается в размещении пользовательских библиотек в соответствующие каталоги. Возможны два вида пользовательских библиотек: общие и частные. Общие библиотеки пользователей должны находиться в каталоге %AS_DIR%\lib. Там находится библиотека стандартных функций lib.dll. Свои частные библиотеки каждый пользователь должен помещать в специально отведённый для них каталог %AS_DIR%\lib\<имя_пользователя>.
Каждый пользователь имеет доступ только к своим частным библиотекам. Общие библиотеки доступны всем пользователям.
При подключении библиотеки с помощью оператора OPEN LIBRARY <имя_библиотеки> интерпретатор в поисках данной библиотеки просматривает сначала каталог, содержащий частные библиотеки пользователя. Если библиотека с таким именем не найдена, то интерпретатор просматривает каталог, в котором хранятся общие библиотеки пользователей. В случае совпадения имён частной и общей библиотек, интерпретатор подключает частную библиотеку пользователя.
При вызове функции интерпретатор ищет её в подключенной библиотеке. Если функция не найдена, то интерпретатор ищет её в библиотеке стандартных функций, которая подключается автоматически.
Замечание. Одновременно можно подключить только одну библиотеку (не считая библиотеки стандартных функций).
Обмен данными между интерпретатором ASPL и внешними хранимыми процедурами
Обмен данными между интерпретатором ASPL и внешними хранимыми процедурами осуществляется через внутренний стек интерпретатора. Перед вызовом внешней процедуры интерпретатор помещает в стек входные значения процедуры и передает ей управление. Внешняя процедура извлекает входные значения из стека, обрабатывает их, помещает в стек выходные значения и передает управление интерпретатору. Интерпретатор извлекает из стека выходные значения и обрабатывает их.
Создание пользовательских библиотек
Пользовательские библиотеки создаются по общим правилам построения динамически подключаемых библиотек (см. приложение). Наряду с этим есть некоторые особенности, относящиеся к передаче входных и выходных значений библиотечных функций. Любая библиотечная функция должна в явном виде извлекать входные значения из стека и помещать в стек выходные значения. Это объясняется тем, что интерпретатор заранее не может знать о количестве передаваемых параметров. Поэтому перед передачей управления функции интерпретатор помещает в стек входные значения, не проверяя их количество, а функция, получив управление, извлекает входные
значения из стека и проверяет их число. После вычисления выходных значений функция должна поместить их в стек. Для того чтобы отличать библиотечные функции от стандартных функций языка C, все функции, входящие в пользовательские библиотеки, должны иметь префикс as_ и быть объявлены следующим образом:void as_<имя_функции>(void)
Для извлечения входных значений из стека служит функция Pop(). Она возвращает значение типа VALUE, так как в стеке содержатся значения только этого типа. Для того чтобы привести переменную типа VALUE к требуемому типу, нужно воспользоваться одной из функций вида GetAs<Type>(VALUE *), где Type - требуемый тип. Проверка стека на наличие в нём значений осуществляется с помощью функции IsStackEmpty().
После вычисления выходных значений, они должны быть приведены к типу VALUE с помощью одной из функций вида Make<Type>(VALUE*, <Type>). Затем выходные значения заносятся в стек с помощью функции Push(VALUE*) или Return (VALUE*). Эти функции идентичны.
Замечание. Прежде чем помещать выходные значения в стек, нужно убедиться в том, что он пуст.
Объявление типа VALUE и прототипы всех функций обработки параметров находятся в файле assdk.h. Приведем их список:
Назначение:
проверяет стек на наличие в нём элементов.Синтаксис:
int IsStackEmpty( void );
Возвращаемые значения:
1, если стек пуст, 0 - в противном случае.Назначение
: извлекает элемент из стека.Синтаксис
:VALUE Pop( void );
Возвращаемые
значения: переменная типа VALUE.Назначение: помещают элемент в стек.
Синтаксис:
void Push( VALUE* v );
void Return( VALUE* v );
Входные значения:
v - переменная типа VALUE со значением элемента.
Назначение:
приводят переменную типа VALUE к типу <Type>.Синтаксис:
<CType> GetAs<Type>(VALUE* v);
Здесь CType и Type - одни из следующих типов:
CType |
Type |
char |
Char |
short int |
Short |
long int |
Long |
float |
Float |
double |
Double |
char * |
String |
Замечание
. Типы Short, Float не поддерживаются интерпретатором ASPL. Тип Long соответствует типу Integer языка ASPL.Входные значения:
v - переменная типа VALUE.
Возвращаемые значения: переменная типа <Type>.
Назначение:
приводят переменную типа <Type> к типу VALUE.Синтаксис:
void Make<Type>( VALUE* v, <CType> c );
Здесь значения CType и Type совпадают с рассмотренными выше.
Входные значения:
v - переменная типа VALUE;
c - переменная типа <Type>.
В качестве примера рассмотрим функцию нахождения синуса as_sin, содержащуюся в библиотеке стандартных функций lib.dll.
void as
_sin( void ) /*Объявление функции*/{
VALUE v;
double f;
if ( IsStackEmpty() ) /*Проверка числа параметров*/
AS_ERROR("Illegal use of sin function.");
v = Pop(); /*Извлечение входного значения из стека*/
f = GetAsDouble( &v ); /*Приведение входного значения к типу Double*/
errno = 0;
f = sin(f);
if ( errno != 0 )
AS_ERROR("SIN error."); /*Сообщение об ошибке и выход из функции*/
Drop(&v);
MakeDouble( &v, f ); /*Приведение выходного значения к типу VALUE*/
Return( &v ); /*Занесение выходного значения в стек*/
}
Использование пользовательских библиотек
Для использования функций из пользовательской библиотеки соответствующая библиотека должна быть подключена с помощью оператора OPEN LIBRARY <имя_библиотеки>. После этого функции, входящие в библиотеку, могут быть вызваны оператором CALL:
CALL <имя_функции>(<список_входных_значений>) RETURNING <список_возвращаемых_значений>.
Если функция возвращает только одно значение, то её можно использовать в выражениях, например, f = sin(x).
Замечание. При вызове функций, входящих в пользовательские библиотеки, к имени функции не нужно добавлять префикс as_, так как интерпретатор делает это автоматически. Например, рассмотренную выше функцию as_sin следует вызывать CALL sin(x) RETURNING x. Подробнее о функциях стандартной библиотеки см. приложение 3
.4. ЯЗЫК ХРАНИМЫХ ПРОЦЕДУР СЕРВЕРА ПРИЛОЖЕНИЙ
нутренние хранимые процедуры создаются на языке хранимых процедур сервера приложений (ASPL - Application Server Program Language), который описан ниже. В текущей версии ASPL является интерпретируемым.Внешние хранимые процедуры создаются на произвольном языке программирования, для которого существует библиотека функций доступа (
SDK) к серверу приложений (подробнее см. главу "Руководство программиста" раздел "Внешние хранимые процедуры").Как отмечалось выше, для каждого пользователя сервера приложений существует три класса доступных ему функций и процедур:
Стандартные функции реализованы стандартной библиотекой на
ASPL, хранимые процедуры и функции общего назначения создаются разработчиками (могут быть внешними и внутренними), пользовательские хранимые процедуры (могут быть внешними и внутренними) создаются для каждого пользователя конкретно и могут переопределять хранимые процедуры общего назначения.В отличие от хранимых процедур СУБД, хранимые процедуры сервера приложений не являются частью БД, а хранятся и исполняются исключительно сервером приложений. Синтаксис языка максимально приближен к синтаксису языка
SPL с целью облегчения переноса существующих хранимых процедур БД в архитектуру сервера приложений. В некоторых случаях хранимые процедуры могут быть перенесены без дополнительной модификации исходного кода.Текст хранимой процедуры сервера приложений состоит из
SQL-операторов и операторов языка сервера приложений. Операторы SQL никаким образом не выделяются из исходного текста процедуры и используются как для извлечения (сохранения, модификации) информации из СУБД, так и выполнения произвольных действий с данными и структурой БД.Раздел содержит справочную информацию по использованию операторов языка хранимых процедур сервера приложений.
4.1 НАИБОЛЕЕ ВАЖНЫЕ ОТЛИЧИЯ ОТ SPL
Я
зык содержит следующие важнейшие отличия от SPL:4.2 ЭЛЕМЕНТЫ ЯЗЫКА
Э
лементы языка - это имена таблиц, выражения, т.е. те конструкции, которые часто повторяются в синтаксических диаграммах, описывающих язык.Ниже следует описание каждого элемента языка, снабженное синтаксической диаграммой, правилами использования и примерами.
Следует заметить, что приведенные синтаксические диаграммы описывают только элементы языка хранимых процедур и ни в коем случае не элементы языка
SQL. Для элементов SQL такие диаграммы значительно шире и могут быть получены из справочников по SQL.Список элементов языка:
Используется при передаче определенного значения вызываемой процедуре.
СИНТАКСИС
Передача значений вызываемой процедуре происходит по значению. Количество передаваемых значений должно совпадать с количеством соответствующих формальных параметров. Пример:
CALL add_col ('customer','newint','integer')
;Выражение представляет собой набор данных и операций, производимых с ними. Выражения часто используются в операторах языка хранимых процедур и SQL-операторах
СИНТАКИС
ИСПОЛЬЗОВАНИЕ
Выражения могут связываться операциями. Запрещается использование агрегатных выражений в разделе
WHERE, если они используются в подзапросе.АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ
В языке поддерживаются стандартные арифметические операции: +, -, *, /. Если оба аргумента арифметической операции одного типа, - результат имеет тот же самый тип. Если типы аргументов различаются, то производится неявное преобразование типов аргументов и результат получает тип, определяемый следующей таблицей:
CHAR |
INTEGER |
FLOAT |
STRING |
|
CHAR |
CHAR |
INTEGER |
FLOAT |
STRING |
INTEGER |
INTEGER |
INTEGER |
FLOAT |
INTEGER |
FLOAT |
FLOAT |
FLOAT |
FLOAT |
FLOAT |
STRING |
STR |
INTEGER |
FLOAT |
STRING |
Для определения типа результата следует выбрать строку с типом первого аргумента, и столбец с типом второго аргумента. Тип результата будет находиться на пересечении строки и столбца.
ОПЕРАТОР УСЕЧЕНИЯ
Оператор
CLIPPED отбрасывает все конечные символы, не являющиеся алфавитно-цифровыми (пробелы, табуляции, символы перевода строк и возврата каретки). Если тип выражения не строковый, то операция над ним не выполняется. Иначе результатом будет являться строковое значение. Если операнд имеет значение NULL, результатом будет являться NULL. Пример:lname CLIPPED
ОПЕРАТОР КОНКАТЕНАЦИИ
С помощью оператора (
||) можно связать два произвольных выражения. Результатом будет являться строковое значение. Если один из операндов имеет значение NULL, результатом будет являться NULL. Примеры:lname || zipcode
file_variable || '.dbg'
'Date:' || TODAY
ВЫРАЖЕНИЕ-КОНСТАНТА
На следующей диаграмме показаны возможные определения констант.
USER
определяет имя пользователя, под которым зарегистрировался клиент.TODAY
определяет текущую дату в строковом формате.CURRENT
определяет текущее время в строковом формате.Примеры выражений-констант:
TODAY
'His first name is'
CURRENT
ВЫЗОВ ФУНКЦИИ В ВЫРАЖЕНИИ
Внутри выражения может быть осуществлен вызов стандартной или пользовательской функции (процедуры), возвращающей одно значение.
Примеры:
LENGTH ('abc') + LENGTH (pvar)
HEX (customer_num)
HEX (LENGTH(123))
TAN (radians)
ABS (-32)
EXP (4,3)
MOD (10,3)
АЛГЕБРАИЧЕСКИЕ ФУНКЦИИ
Алгебраические функции используют один или более аргументов.
Функция
ABSВозвращает абсолютное значение выражения.
Функция
MODВозвращает остаток от деления по модулю.
Функция POW
Возвращает значение базы в степени экспоненты.
Функция
ROOTВозвращает квадратный корень аргумента.
Функция
ROUNDВозвращает округленное значение аргумента.
Функция
SQRTВозвращает квадратный корень аргумента.
Функция
TRUNCВозвращает усеченное значение аргумента.
ЭКСПОНЕНЦИАЛЬНЫЕ И ЛОГАРИФМИЧЕСКИЕ ФУНКЦИИ
Функции используют один или более аргументов.
Функция
EXPВозвращает экспоненциальное значение аргумента.
Функция
LOGNВозвращает натуральный логарифм аргумента.
Функция LOG10
Возвращает десятичный логарифм аргумента.
ФУНКЦИЯ HEX
Возвращает строковое значение целого аргумента в шестнадцатеричной нотации.
ТРИГОНОМЕТРИЧЕСКИЕ ФУНКЦИИ
Функции используют один или более аргументов.
Функции рассматривают аргументы как значения, указанные в радианах. Формулы для перевода из градусов в радианы и наоборот:
радианы = градусы * Пи / 180; градусы = радианы * 180 / Пи
Функция
COSВозвращает значение косинуса для аргумента в радианах.
Функция
SINВозвращает значение синуса для аргумента в радианах.
Функция
TANВозвращает значение тангенса для аргумента в радианах.
Функция
ACOSВозвращает значение арккосинуса для аргумента в радианах.
Функция
ASINВозвращает значение арксинуса для аргумента в радианах.
Функция
ATANВозвращает значение арктангенса для аргумента в радианах.
Используются для поименования переменных, курсоров, процедур и т.д.
ИСПОЛЬЗОВАНИЕ
В отличие от СУБД, игнорирующих регистр, в котором написан оператор
SQL, СП чувствителен к регистру указанных идентификаторов.Максимальная длина идентификатора не ограничена. Не следует использовать в качестве идентификаторов следующие слова в любых регистрах, зарезервированные в
SQL:
ADA all and any as asc authorization avg begin betwe/en by char character check close cobol commit continue count create current cursor dec decimal |
declare delete desc distinct double end escape exec execute exists fetch float for fortran found from go goto group having in indicator insert int |
into integer is language like max min module not null numeric of on open option or order pascal pli precision primary procedure privileges public |
real rollback schema section select set smallint some sql sqlcode sqlerror sum table to union unique update user values view whenever where with work |
Также запрещено использование в качестве идентификаторов ключевых слов языка хранимых процедур СП:
ALL
AND
AS
BEGIN
CALL
CHAR
CLIPPED
CLOSE
COMMIT
CONNECT
CONTINUE
CURRENT
CURSOR
DECLARE
DEFINE
DELETE
DIGIT
DISCONNECT
ELSE
END
EXECUTE
EXIT
FETCH
FLOAT
FOR
FOREACH
FREE
FROM
FUNCTION
IF
IMMEDIATE
INSERT
INTEGER
INTO
IS
LET
LETTER
LIBRARY
NOT
NULL
OPEN
OR
PREPARE
PRINTS
PROCEDURE
RETURN
RETURNING
ROLLBACK
SELECT
SEND
SQLCODE
SQLSTATE
STEP
STRING
SYSTEM
THEN
TO
UPDATE
USER
USING
WHILE
WORK
ОПЕРАТОР ОТНОШЕНИЯ
Используется для количественного сравнения значений двух выражений.
СИНТАКСИС
<
означает меньше;<=
означает меньше либо равно;>
означает больше;>=
означает больше либо равно;=
означает равно;<>
означает не равно;!=
означает не равно.ИСПОЛЬЗОВАНИЕ
Для типов данных
CHAR и STRING сравнение производится на основании таблицы символов и использовании кодировки, в рамках которой указана таблица предшествования.Строки являются частью выражений.
СИНТАКСИС
ИСПОЛЬЗОВАНИЕ
Строка не может содержать символов новой строки.
Используется для указания типа данных переменных в хранимых процедурах.
СИНТАКСИС
ИСПОЛЬЗОВАНИЕ
Сервер приложений поддерживает следующие типы данных:
INTEGER
Переменная типа
INTEGER может хранить произвольное целое число в диапазоне, определенном для переменной типа long int в языке С. Арифметические операции с целыми числами приводят к целому результату.FLOAT
Переменная типа
CHAR может хранить любое вещественное значение в формате с плавающей запятой. Количество значащих цифр совпадает с количеством значащих цифр типа double в языке C. Правила записи числа в формате FLOAT приведены далее в разделе "Число". Арифметические операции с вещественными числами приводят к вещественному результату.CHAR
Переменная типа
CHAR может хранить любую строку ASCII символов. Длина строки может изменяться от 1 до значения, варьирующегося в зависимости от свободного пространства операционной системы. При описании переменной возможны два варианта:DEFINE var CHAR;
и
DEFINE var
CHAR(n);В первом случае описана переменная, содержащая только один символ, во втором произвольное их количество. В текущей реализации значение
n не является предельным значением для длины сохраняемого значения. При присваивании переменной строкового значения длины m>n происходит неявное переопределение типа данных для переменной:DEFINE var CHAR(m);
В текущей реализации СП не устанавливается различий между типом данных
CHAR(n) и STRING.STRING
В текущей реализации полностью идентичен типу
CHAR(n).СТРУКТУРИРОВАННЫЙ ТИП ДАННЫХ ARRAY
Данный тип представляет собой многомерный массив переменных одинакового типа. Переменные могут быть любого типа, поддерживаемого сервером приложений.
Синтаксис
Использование
Для доступа к элементам массива используются переменные или выражения целого типа, заключенные в квадратные скобки. Например,
DEFINE A ARRAY[5][5] OF FLOAT;
A[i][j]
означает j-й элемент в i-й строке двумерного массива.Переменные
i и j могут меняться только в пределах тех значений, которые указаны при описании массива.Условия используются для проверки данных на соответствие ими определенным условиям.
СИНТАКСИС
ИСПОЛЬЗОВАНИЕ
Условие - это набор из одного или многих условий, произвольно соединенных операторами
AND и OR, NOT.ОПЕРАТОР NOT
Оператор
NOT инвертирует значение, вычисляемое условным оператором. Правила вычисления с оператором NOT:Символ (?) соответствует неопределенному значению,
T - истине, F - ложь.ОПЕРАТОРЫ AND И OR
Операторы выполняют логические операции по следующей схеме:
Символ (?) соответствует неопределенному значению,
T - истине, F - ложь.УСЛОВИЯ СРАВНЕНИЯ
В языке поддерживаются следующие типы условных сравнений:
Такие типы операторов называют булевыми выражениями. Содержательно, булево выражение представляет собой целое число. Нолю соответствует ложь, все остальные значения рассматриваются как истина. Оператор
IS NULL возвращает истину, если значение выражения является NULL.Примеры:
city = 'San'
city IS NULL 'San'
quantity <= 3
customer_num <> 105
customer_num != 105
Число может представлять собой целое или вещественное значение.
СИНТАКСИС
ИСПОЛЬЗОВАНИЕ
Числа не могут содержать запятую как разделитель дробной и целой части. Вместо нее используется точка.
4.3 СОЗДАНИЕ ХРАНИМЫХ ПРОЦЕДУР
С
оздание хранимой процедуры производится сервером приложений после указания исходного текста процедуры. В общем виде хранимая процедура состоит из заголовка, тела процедуры, образованного операторами и конца процедуры.СИНТАКИС
Пример описания хранимой процедуры.
FUNCTION not_much();
DEFINE i, j, k INTEGER;
CALL yes_args(0);
CALL yes_args(1) RETURNING i;
CALL yes_args(3) RETURNING i, j, k;
END FUNCTION;
Обычно, хранимые процедуры сервера приложений создает администратор СП. Однако, в процессе работы клиентское приложение может создавать и вызывать собственные процедуры.
Я
зык хранимых процедур включает в себя операторы следующих типов:Декларативные операторы предназначены для описания структуры процедуры и переменных.
Процедурные операторы выполняют определенные действиями по отношению к переменным или порядку выполнения процедуры.
Операторы
SQL (подмножество операторов SQL) являются подмножеством операторов языка. Они предназначены в первую очередь для обмена данными между СУБД и хранимой процедурой.4.5 ПРЕДОПРЕДЕЛЕННЫЕ ПСЕВДОПЕРЕМЕННЫЕ
Я
зык содержит набор следующих псевдопеременных:SQLCODE
содержит код завершения последнего выполненного SQL-оператора.SQLSTATE
содержит код завершения последнего выполненного SQL-оператора по спецификации X/Open.USER
определяет имя пользователя, под которым зарегистрировался клиент.TODAY
определяет текущую дату в строковом формате.CURRENT
определяет текущее время в строковом формате.Особенности взаимодействия КЛИЕНТОВ с сервером приложений в FoxPro 2.6.
Д
ля осуществления взаимодействия с сервером приложений в FoxPro 2.6 необходимо наличие двух файлов:Запуск среды FoxPro осуществляется с помощью программы foxconn.exe, в качестве параметра которой необходимо передать полный путь к исполняемому файлу fox.exe. Пример:
foxconn.exe c:\fpd26\fox.exe
После запуска среды FoxPro необходимо подключить библиотеку функций взаимодействия с сервером приложений. Для этого нужно выполнить команду SET LIBRARY TO foxgen. Файл foxgen.plb должен находиться в текущем каталоге. После подключения библиотеки, можно использовать содержащиеся в ней функции.
ФУНКЦИИ СТАНДАРТНОЙ БИБЛИОТЕКИ
Р
ассмотрим функции, составляющие стандартную библиотеку интерпретатора сервера приложений.Назначение
: вычисляет тригонометрическую функцию косинус в радианах.Вызов функции:
CALL cos(FLOAT) RETURNING FLOAT;
Назначение
: вычисляет тригонометрическую функцию синус в радианах.Вызов функции:
CALL sin(FLOAT) RETURNING FLOAT;
Назначение
: вычисляет тригонометрическую функцию тангенс в радианах.Вызов функции:
CALL tan(FLOAT) RETURNING FLOAT;
Назначение
: вычисляет тригонометрическую функцию арккосинус в радианах.Вызов функции:
CALL acos(FLOAT) RETURNING FLOAT;
Назначение
: вычисляет тригонометрическую функцию арксинус в радианах.Вызов функции:
CALL asin(FLOAT) RETURNING FLOAT;
Назначение
: вычисляет тригонометрическую функцию арктангенс в радианах.Вызов функции:
CALL atan(FLOAT) RETURNING FLOAT;
Назначение
: вычисляет экспоненциальное значение аргумента.Вызов функции
:CALL exp(FLOAT) RETURNING FLOAT;
Назначение
: вычисляет натуральный логарифм аргумента.Вызов функции
:CALL log(FLOAT) RETURNING FLOAT;
Назначение
: вычисляет десятичный логарифм аргумента.Вызов функции
:CALL log10(FLOAT) RETURNING FLOAT;
Назначение
: вычисляет квадратный корень аргумента.Вызов функции:
CALL sqrt(FLOAT) RETURNING FLOAT;
Назначение
: открывает файл в заданном режиме:"R" - режим чтения;
"W" - режим записи;
"RW", "WR" - режим чтения и записи.
Вызов функции:
DEFINE handle INTEGER; /* дескриптор файла */
DEFINE fname STRING; /* имя файла */
DEFINE mode STRING; /* режим открытия */
:
CALL open(fname, mode) RETURNING handle;
или
CALL open(fname) RETURNING handle;
Назначение
: открывает программный канал.Вызов функции:
DEFINE hIn, hOut INTEGER; /* входной и выходной дескрипторы
канала */
DEFINE PipeName STRING; /* имя канала */
:
CALL pipe() RETURNING hIn, hOut;
или
CALL pipe(PipeName) RETURNING hIn, hOut;
Назначение
: для совместимости с Windows. Выполняет соединение с каналом.Вызов функции:
DEFINE pipedes INTEGER; /* дескриптор канала */
:
CALL pcon(pipedes);
Назначение
: для совместимости с Windows. Выполняет отсоединение от канала.Вызов функции:
DEFINE pipedes INTEGER; /* дескриптор канала */
:
CALL pdcon(pipedes);
Назначение
: читает данные из программного канала.Вызов функции:
DEFINE hOut INTEGER; /* дескриптор канала */
DEFINE size INTEGER; /* требуемое количество байт */
DEFINE s STRING; /* строка с прочитанными данными */
:
CALL read(hOut, size) RETURNING s;
или
CALL read(hOut) RETURNING s;
Назначение
: записывает данные в программный канал.Вызов функции:
DEFINE hOut INTEGER; /* дескриптор канала */
DEFINE size INTEGER; /* требуемое количество байт */
DEFINE s STRING; /* строка с записываемыми данными */
:
CALL write(hOut, s, size) RETURNING size;
или
CALL write(hOut, s) RETURNING size;
Назначение
: закрывает файл или программный канал.Вызов функции:
DEFINE handle INTEGER; /* дескриптор файла или канала */
:
CALL close(handle);
Назначение
: получает дескриптор семафора.Вызов функции:
DEFINE semname STRING; /* имя семафора */
DEFINE semcount INTEGER; /* количество состояний семафора */
DEFINE semhandle INTEGER; /* дескриптор семафора */
:
CALL semget(semname) RETURNING semhandle;
или
CALL semget(semname, semcount) RETURNING semhandle;
Назначение
: блокирует семафор.Вызов функции:
DEFINE semhandle INTEGER; /* дескриптор семафора */
DEFINE timeout INTEGER; /* задержка в миллисекундах */
DEFINE lock_result INTEGER; /* 1 - семафор заблокирован,
0 - семафор не заблокирован*/
:
CALL semlock(semhandle) RETURNING lock_result;
или
CALL semlock(semhandle, timeout) RETURNING lock_result;
Назначение
: определяет, можно ли заблокировать семафор.Вызов функции:
DEFINE semhandle INTEGER; /* дескриптор семафора */
DEFINE i INTEGER; /* 1 - семафор можно заблокировать,
0 - семафор нельзя заблокировать*/
:
LET i = semtry(semhandle);
Назначение
: освобождает семафор.Вызов функции:
DEFINE semhandle INTEGER; /* дескриптор семафора */
DEFINE unlock_result INTEGER; /* 1 - семафор освобожден,
0 - семафор не освобожден */
:
CALL semunlock(semhandle) RETURNING unlock_result;
Назначение
: закрывает семафор.Вызов функции:
DEFINE semhandle INTEGER; /* дескриптор семафора */
:
CALL semclose(semhandle);
Назначение
: возвращает дескриптор стандартного входа.Вызов функции:
DEFINE stdin_handle INTEGER; /* дескриптор стандартного
входа */
:
CALL stdin() RETURNING stdin_handle;
Назначение
: ждет изменений состояния дескриптора стандартного входа.Вызов функции:
DEFINE h ARRAY [*] OF INTEGER; /* массив дескрипторов */
DEFINE timeout INTEGER; /* задержка */
DEFINE ActiveHandleNum INTEGER; /* индекс дескриптора с
изменившимся состоянием */
:
CALL select(h) RETURNING ActiveHandleNum;
или
CALL select(h, timeout) RETURNING ActiveHandleNum;
Назначение
: возвращает идентификатор процесса.Вызов функции:
DEFINE mypid INTEGER; /* идентификатор процесса */
:
LET mypid = getpid();
Назначение
: останавливает работу процесса на определенное время.Вызов функции:
DEFINE timeout INTEGER; /* задержка в миллисекундах */
:
CALL sleep(timeout);
Назначение
: проверяет, есть ли символ на стандартном входе.Вызов функции:
DEFINE i INTEGER; /* 1
- символ есть,0 - символа нет */
:
LET i = kbhit();
Назначение
: для совместимости с INFORMIX версии меньшей 6. Открывает базу данных.Вызов функции:
DEFINE dbname STRING; /* имя базы данных */
:
CALL database(dbname);
Назначение
: выделяет лексему из строки по ограничителям.Вызов функции:
DEFINE token STRING; /* лексема */
DEFINE str STRING; /* строка */
DEFINE delimiters STRING; /* ограничители */
:
LET token = strtok(str, delimiters);
или
LET token = strtok(delimiters);
Назначение
: определяет длину строки.Вызов функции:
DEFINE len INTEGER; /* длина строки */
DEFINE str STRING; /* строка */
:
LET len = strlen(str);
или
CALL strlen(str) RETURNING len;
О
ператоры записываются в произвольном регистре, разделителем между операторами является символ (;). На одной строке в тексте программы может находиться произвольное количество операторов. Один оператор может занимать более одной строки в тексте программы.Для улучшения читабельности программы рекомендуется записывать операторы в верхнем регистре, а все остальные идентификаторы в нижнем.
Оператор используется для выполнения хранимой процедуры СП или внешней хранимой процедуры СП.
СИНТАКСИС
|
|
|
|
Элемент |
Назначение |
Ограничения |
Синтаксис |
Имя переменной |
Переменная цикла |
Должна быть определена |
Идентификатор |
Имя процедуры |
Уникально идентифицирует вызываемую хранимую процедуру |
Должна быть создана хранимая процедура (внешняя хранимая процедура) с таким именем |
Идентификатор |
ИСПОЛЬЗОВАНИЕ
Оператор используется для вызова хранимой процедуры СП (внешней хранимой процедуры СП).
АРГУМЕНТЫ
Передача аргументов происходит по значению. При вызове хранимой процедуры необходимо передавать в точности такое количество параметров, сколько было указано формальных параметров в ее описании. Передача меньшего или большего количества параметров приведет к ошибке.
ПОЛУЧЕНИЕ ЗНАЧЕНИЙ
Раздел
RETURNING описывает переменные, в которые помещаются результаты работы функции. При отсутствии раздела RETURNING процедура не должна передавать никаких значений.Процедура может возвращать разное количество значений, но требуется, чтобы их количество совпадало с количеством указанных переменных в разделе
RETURNING.В следующем примере показано три вызова хранимой процедуры
yes_args с получением от нее разного количество возвращаемых значений.FUNCTION not_much();
DEFINE i, j, k INTEGER;
CALL yes_args(0);
CALL yes_args(1) RETURNING i;
CALL yes_args(3) RETURNING i, j, k;
END FUNCTION;
Оператор используется для закрытия ставшего ненужным курсора с
SELECT-выражением или курсора для вставки.СИНТАКСИС
Элемент |
Назначение |
Ограничения |
Синтаксис |
Имя курсора |
Указывает иденти-фикатор курсора |
Должен существовать указанный идентификатор |
Идентифи-катор |
Переменная курсора |
Значение содержит идентификатор курсора |
Должен существовать указанный идентификатор |
Идентифи-катор |
ИСПОЛЬЗОВАНИЕ
Закрытие курсора делает невозможным более доступ к нему любыми операторами, кроме
OPEN и FREE, и освобождает ресурсы СП, выделенные для его обработки. Курсор может быть закрыт, если он не был открыт, а также закрыт повторно.Имя курсора может быть указано явно или с помощью переменной. В том случае, если указанный идентификатор является описанной переменной, предполагается, что ее значение является именем курсора. Не рекомендуется давать одинаковые имена переменным и курсорам.
ОБРАБОТКА ОШИБОК
После выполнения оператора, код его завершения доступен через переменные
SQLCODE и SQLSTATE.ССЫЛКИ
Смотри также разделы с описанием операторов
DECLARE, FETCH, FREE, OPEN.Оператор используется для открытия библиотеки внешних хранимых процедур Синтаксис
|
|
|
|
Элемент |
Назначение |
Ограничения |
Синтаксис |
Имя библиотеки |
Указывает имя библиотеки внешних процедур |
Выражение, результат которого - строковая константа |
Идентификатор |
Переменная библиотеки |
Значение содержит имя библиотеки внешних процедур |
Должен существовать указанный идентификатор |
Идентификатор |
ИСПОЛЬЗОВАНИЕ
Закрытие библиотеки делает невозможным более доступ к ней любыми операторами, содержащими ссылку на функции в ее составе.
Имя библиотеки может быть указано явно - с использованием выражений, или с помощью переменной. В том случае если указанный идентификатор является описанной переменной, предполагается, что ее значение является именем библиотеки.
Оператор используется для перехода к следующей итерации циклов
FOR, WHILE или FOREACH.СИНТАКСИС
ИСПОЛЬЗОВАНИЕ
После выполнения оператора управление передается на начало тела самого вложенного цикла указанного типа. Все операторы после
CONTINUE в рамках этого цикла пропускаются.В следующем примере оператор
INSERT не выполнится при i равном 11.FUNCTION loop_skip();
DEFINE i INTEGER;
.
.
FOR i = 1 TO 15 STEP 2
IF i == 11 THEN
CONTINUE FOR;
END IF;
INSERT INTO testtable VALUES( i, null );
END FOR;
RETURN i;
END FUNCTION;
Оператор
CONTINUE сгенерирует ошибку при использовании его за пределами цикла.Оператор используется для определения курсора, определяющего активный набор строк, возвращенный оператором
SELECT.СИНТАКСИС
Элемент |
Назначение |
Ограничения |
Синтаксис |
Имя курсора |
Идентифицирует курсор |
Уникально в переделах процедуры |
Идентификатор |
Переменная курсора |
Значение содержит идентификатор курсора |
Должен существовать указанный идентификатор |
Идентификатор |
Имя переменной |
Содержит SQL-оператор |
Должна быть описана |
Идентификатор |
ИСПОЛЬЗОВАНИЕ
Оператор
DECLARE ассоциирует курсор с SQL-оператором и назначает курсору уникальный в пределах процедуры идентификатор. До начала работы с курсором он должен быть описан.Имя курсора может быть указано явно или с помощью переменной. В том случае, если указанный идентификатор является описанной переменной, предполагается, что ее значение является именем курсора. Не рекомендуется давать одинаковые имена переменным и курсорам.
Иначе за курсором закрепляется указанный идентификатор.
ОБРАБОТКА ОШИБОК
После выполнения оператора, код его завершения доступен через переменные
SQLCODE и SQLSTATE.ССЫЛКИ
Смотри также разделы с описанием операторов
CLOSE, FETCH, FREE, OPEN.Оператор используется для описания переменных и ассоциирования с ними определенных типов данных.
СИНТАКСИС
Элемент |
Назначение |
Ограничения |
Синтаксис |
Имя переменной |
Переменная цикла |
Должна быть определена |
Идентификатор |
Тип данных |
Указывает тип переменной |
Ограничено типами данных, поддерживаемыми СП |
Зарезервирован-ное слово |
ИСПОЛЬЗОВАНИЕ
Оператор
DEFINE не является исполняемым и указывается в любом месте процедуры до первого исполняемого оператора, использующего объявляемые переменные. Область действия описанных переменных - все тело процедуры.Переменная с одним и тем же именем может быть в дальнейшем переописана с помощью оператора REDEFINE. После описания переменная получает значение
NULL. Пример:FUNCTION example1();
DEFINE n INTEGER; DEFINE j INTEGER;
.
DEFINE a CHAR;
DEFINE b ARRAY[5][10] OF FLOAT;
. . .
Оператор используется для выхода из циклов
FOR, WHILE и FOREACH.СИНТАКСИС
ИСПОЛЬЗОВАНИЕ
Оператор принудительно завершает выполнение предыдущего соответствующего оператора цикла. Выполнение продолжится с оператора, непосредственного следующего за телом цикла.
Оператор
EXIT сгенерирует ошибку при использовании его за пределами цикла или при отсутствии объявления цикла соответствующего типа.В следующем примере цикл FOR прекратит выполнение, когда в цикле
WHILE переменная i достигнет значения 5.FUNCTION ex_cont_ex();
DEFINE i,j,s INTEGER;
FOR j = 1 TO 20
IF j>10 THEN
CONTINUE FOR;
END IF;
LET i = j;
LET s = 0;
WHILE i > 0
LET i = i - 1;
IF i == 5 THEN
EXIT FOR;
END IF;
END WHILE;
END FOR;
END FUNCTION;
Оператор используется для перемещения строки курсора на новую позицию в активном наборе данных курсора и извлечения данных в указанные переменные.
СИНТАКСИС
Элемент |
Назначение |
Ограничения |
Синтаксис |
Имя курсора |
Указывает идентификатор курсора |
Должен существовать указанный идентификатор |
Идентифи-катор |
Переменная курсора |
Значение содержит идентификатор курсора |
Должен существовать указанный идентификатор |
Идентифи-катор |
ИСПОЛЬЗОВАНИЕ
Оператор
FETCH используется для обработки запросов, возвращающих более одной строки результата. Всего таких операторов четыре: FETCH, DECLARE, OPEN, CLOSE.Последовательность их использования в программе должна быть такой:
После выполнения оператора значения из текущей строки активного набора данных заносится в список переменных, указанных в операторе.
Имя курсора может быть указано явно, или с помощью переменной. В том случае если указанный идентификатор является описанной переменной, предполагается, что ее значение является именем курсора. Не рекомендуется давать одинаковые имена переменным и курсорам.
ОБРАБОТКА ОШИБОК
После выполнения оператора, код его завершения доступен через переменные
SQLCODE и SQLSTATE.ССЫЛКИ
Смотри также разделы с описанием операторов
CLOSE, DECLARE, FREE, OPEN.Оператор используется для создания безусловных циклов.
СИНТАКСИС
Элемент |
Назначение |
Ограничения |
Синтаксис |
Выражение |
Используется для задания начального, конечного значений переменной цикла и шага цикла |
Должно быть целочисленным |
Выражение |
Имя переменной |
Переменная цикла |
Должна быть определена |
Идентификатор |
ИСПОЛЬЗОВАНИЕ
Выражения, определяющие начальное и конечное значение для переменной цикла, вычисляются перед началом цикла. Оператор будет выполняться пока в теле цикла не встретится оператор
EXIT FOR или переменная цикла не примет последовательно все значения от начального до конечного в соответствии с шагом.Пример использования вложенных циклов.
FUNCTION for_ex();
DEFINE i, j INTEGER;
FOR i = 1 TO 10
FOR j = i TO 10
INSERT INTO tab VALUES( i, j );
END FOR;
END FOR;
END FUNCTION;
Оператор используется для получения набора многострочных данных.
СИНТАКСИС
Элемент |
Назначение |
Ограничения |
Синтаксис |
Имя курсора |
Идентифицирует курсор |
Уникально в переделах процедуры |
Идентификатор |
Переменная курсора |
Значение содержит идентификатор курсора |
Должен существовать указанный идентификатор |
Идентификатор |
ИСПОЛЬЗОВАНИЕ
Использование оператора
FOREACH аналогично использованию аппарата курсоров. При выполнении оператора сервер приложений выполняет следующие операции:Операторы
FOREACH могут быть вложенными. Ограничений на уровень вложенности нет.Имя курсора может быть указано явно или с помощью переменной. В том случае, если указанный идентификатор является описанной переменной, предполагается, что ее значение является именем курсора. Не рекомендуется давать одинаковые имена переменным и курсорам.
В следующих примерах показано использование оператора
FOREACH.FUNCTION foreach_ex();
DEFINE i, j INTEGER;
FOREACH SELECT c1 INTO i FROM tab ORDER BY 1
INSERT INTO tab2 VALUES ( i );
END FOREACH;
FOREACH cur1 FOR SELECT c2, c3 INTO i, j FROM tab
IF j > 100 THEN
DELETE FROM tab WHERE CURRENT OF cur1;
CONTINUE FOREACH;
END IF;
UPDATE tab SET c2 = c2 + 10 WHERE CURRENT OF cur1;
END FOREACH;
FOREACH EXECUTE PROCEDURE bar(10,20) INTO i
INSERT INTO tab2 VALUES( i );
END FOREACH;
END FUNCTION;
ОБРАБОТКА ОШИБОК
После выполнения оператора, код его завершения доступен через переменные
SQLCODE и SQLSTATE.ССЫЛКИ
Смотри также разделы с описанием операторов
CLOSE, DECLARE, FETCH, FREE, OPEN.Оператор используется для освобождения ресурсов, использованных курсором.
СИНТАКСИС
Элемент |
Назначение |
Ограничения |
Синтаксис |
Имя курсора |
Идентифицирует курсор |
Уникально в переделах процедуры |
Идентифи-катор |
Переменная курсора |
Значение содержит идентификатор курсора |
Должен существовать указанный идентификатор |
Идентифи-катор |
ИСПОЛЬЗОВАНИЕ
Оператор освобождает ресурсы, занятые сервером приложений и СУБД. Рекомендуется закрывать курсор перед использованием оператора
FREE. После использования оператора FREE курсор не может быть открыт оператором OPEN снова. Для этого необходимо снова использовать оператор DECLARE.Имя курсора может быть указано явно или с помощью переменной. В том случае, если указанный идентификатор является описанной переменной, предполагается, что ее значение является именем курсора. Не рекомендуется давать одинаковые имена переменным и курсорам.
ОБРАБОТКА ОШИБОК
После выполнения оператора, код его завершения доступен через переменные
SQLCODE и SQLSTATE.ССЫЛКИ
Смотри также разделы с описанием операторов
CLOSE, DECLARE, FETCH, OPEN.Оператор используется для создания альтернативных ветвей выполнения программы.
СИНТАКСИС
ИСПОЛЬЗОВАНИЕ
В зависимости от значения выражения выполняется ветка
THEN или ELSE. Значение выражения приводится к целому типу. Если результат приведения равен 0, выполняется ветка ELSE. Значение NULL приводится к нулю.В следующем примере производится сравнение двух строковых переменных:
FUNCTION str_compare( str1, str2 );
DEFINE result INTEGER;
IF str1 > str2 THEN
LET result = 1;
ELSE
IF str1 < str2 THEN
LET result = -1;
ELSE
LET result = 0;
END IF;
END IF;
RETURN result;
END FUNCTION ;
Оператор используется для присваивания переменным и элементам массива новых значений. Этот оператор может быть опущен.
СИНТАКСИС
Элемент |
Назначение |
Ограничения |
Синтаксис |
Имя переменной |
Переменная цикла |
Должна быть определена |
Идентификатор |
ИСПОЛЬЗОВАНИЕ
Элементом выражения может выступать вызов хранимой процедуры.
Примеры:
a
= c + d;LET name = 'test'
|| random();Оператор используется для открытия курсора, ассоциированного с оператором
SELECT или EXECUTE PROCEDURE.СИНТАКСИС
Элемент |
Назначение |
Ограничения |
Синтаксис |
Имя курсора |
Указывает идентификатор курсора |
Должен существовать указанный идентификатор |
Идентифи-катор |
Переменная курсора |
Значение содержит идентификатор курсора |
Должен существовать указанный идентификатор |
Идентифи-катор |
ИСПОЛЬЗОВАНИЕ
Курсор может быть открыт, если он был описан с помощью оператора
DECLARE. В момент открытия курсора ассоциированный с ним SQL-запрос передается СУБД.Имя курсора может быть указано явно или с помощью переменной. В том случае, если указанный идентификатор является описанной переменной, предполагается, что ее значение является именем курсора. Не рекомендуется давать одинаковые имена переменным и курсорам.
Курсор может быть переоткрыт в произвольный момент.
Примеры:
DECLARE curs CURSOR FOR SELECT * FROM orders;
OPEN curs;
DECLARE curs CURSOR FOR EXECUTE PROCEDURE proc();
OPEN curs;
ОБРАБОТКА ОШИБОК
После выполнения оператора, код его завершения доступен через переменные
SQLCODE и SQLSTATE.ССЫЛКИ
Смотри также разделы с описанием операторов
CLOSE, DECLARE, FETCH, FREE.Оператор используется для открытия библиотеки внешних хранимых процедур
.СИНТАКСИС
Элемент |
Назначение |
Ограничения |
Синтаксис |
Имя библиотеки |
Указывает имя библиотеки внешних процедур |
Выражение, результат которого - строковая константа |
Идентифика-тор |
Переменная библиотеки |
Значение содержит имя библиотеки внешних процедур |
Должен существовать указанный идентификатор |
Идентифика-тор |
ИСПОЛЬЗОВАНИЕ
После открытия библиотеки внешних процедур из хранимой процедуры СП можно вызвать любую функцию из открытой библиотеки. Порядок поиска библиотек следующий:
Имя библиотеки может быть указано явно с использованием выражений, или с помощью переменной. В том случае, если указанный идентификатор является описанной переменной, предполагается, что ее значение является именем библиотеки. Курсор может быть переоткрыт в произвольный момент.
В следующем примере открывается внешняя библиотека
math и вызывается внешняя функция bessel:.
.
OPEN LIBRARY 'math';
LET a = bessel(0,1,3);
.
.
Оператор используется для переописания переменных и ассоциирования с ними других типов данных.
СИНТАКСИС
Элемент |
Назначение |
Ограничения |
Синтаксис |
Имя переменной |
Переменная цикла |
Должна быть определена |
Идентификатор |
Тип данных |
Указывает тип переменной |
Ограничено типами данных, поддерживаемыми СП |
Зарезервирован-ное слово |
ИСПОЛЬЗОВАНИЕ
Оператор RE
DEFINE не является исполняемым и указывается в любом месте процедуры для переописания переменных, ранее описанных с помощью оператора DEFINE.Переменная с одним и тем же именем может быть в дальнейшем вновь переописана. После описания переменная получает значение
NULL. Пример:FUNCTION example1();
DEFINE n INTEGER; DEFINE j INTEGER;
. . .
REDEFINE n CHAR;
Оператор используется для определения значений, возвращаемых из процедуры.
СИНТАКСИС
ИСПОЛЬЗОВАНИЕ
Оператор
RETURN может возвратить ноль или более значений вызвавшему процессу. Во время выполнения хранимой процедуры может быть выполнено несколько операторов RETURN. Пример:FUNCTION two( i INTEGER, j INTEGER );
IF i > j THEN
RETURN i, j;
ELSE
RETURN j, i;
END IF;
RETURN i > j;
END FUNCTION;
Оператор используется для передачи пользователю значений выражений.
СИНТАКСИС
Элемент |
Назначение |
Ограничения |
Синтаксис |
Выражение |
Определяет выражение, значение которого будет использоваться при передаче данных |
Нет |
Выражение |
ИСПОЛЬЗОВАНИЕ
Клиенту передаются вычисленные значения выражений. Если выполнение программы происходит из командной строки, результат выдается на экран. Если выполнение программы инициирует сервер приложений, результаты передаются клиенту.
Пример использования:
FUNCTION send_i( i INTEGER );
SEND "sqr(i) == " || i*i,
"cube(i) == " || i*i*i;
END FUNCTION;
Оператор используется для установки активного соединения, идентификатор которого был указан в операторе CONNECT после ключевого слова AS.
СИНТАКСИС
Элемент |
Назначение |
Ограничения |
Синтаксис |
Имя соединения |
Идентифицирует соединение |
Уникально в переделах процедуры |
Идентификатор |
Переменная соединения |
Значение содержит идентификатор соединения |
Должен существовать указанный идентификатор |
Идентификатор |
ИСПОЛЬЗОВАНИЕ
Клиент может установить несколько соединений с разными базами данных. Чтобы переключаться между соединениями, он должен использовать оператор SET CONNECT TO.
Пример использования:
FUNCTION set_connection();
DEFINE s2 STRING;
s2 = 'c2';
CONNECT TO 'db1' as 'c1';
CONNECT TO 'db2' as s2;
SET CONNECT TO 'c1';
END FUNCTION;
Оператор используется для выполнения команд операционной системы.
СИНТАКСИС
Элемент |
Назначение |
Ограничения |
Синтаксис |
Выражение |
Определяет командную строку, содержащую команду операционной системы |
Нет |
Выражение |
Имя переменной |
В первом случае содержит команду операционной системы, Во втором после выполнения команды содержит код ее завершения |
Нет |
Идентифика-тор |
ИСПОЛЬЗОВАНИЕ
Если указанное выражения не является символьным, производится преобразование его значения к символьному. Набор символов передается в качестве команды операционной системе. Выполнение процедуры приостанавливается до завершения выполнения команды ОС. Пример использования:
FUNCTION cat();
DEFINE i INTEGER;
SYSTEM 'ls | wc -l' RETURNING i;
RETURN i;
END FUNCTION;
Оператор используется для создания условных циклов в программе.
СИНТАКСИС
Элемент |
Назначение |
Ограничения |
Синтаксис |
выражение |
определяет возможность следующей итерации |
Нет |
выражение |
ИСПОЛЬЗОВАНИЕ
П
еред началом каждой итерации значение выражения вычисляется заново. Если значение выражения является истиной, следующая итерация цикла выполняется. Здесь выражение имеет тот же смысл, что и выражение в операторе IF.Выполнение оператора может быть досрочно завершено оператором
EXIT WHILE. Пример использования:FUNCTION simp_while();
DEFINE i INTEGER;
LET i = 1;
WHILE i < 10
INSERT INTO tab VALUES ( i, i+1 );
LET i = i + 1;
END WHILE;
END FUNCTION;
ПРИМЕР ИСПОЛЬЗОВАНИЯ СЕРВЕРА ПРИЛОЖЕНИЙ
П
рограммы данного примера показывают один из вариантов использования сервера приложений для реализации упрощенной модели задачи управления технологическими процессами.Для построения этой модели используются преимущества трехзвенной архитектуры, где основная часть работы по обработке информации вынесена на сервер приложений.
Также иллюстрируется создание скриптовой библиотеки функций, обеспечивающей распределенную обработку технологической информации, управление работой клиентской части.
Данный пример может служить основой для построения сложных производственных систем с использованием возможностей сервера приложений.
Ц
елью данной работы является построение приложения, отвечающего следующим требованиям:П
ример представляет собой модель распределенной системы, расположенной на клиентской стороне и на сервере приложений. На клиентской стороне, состоящей из администратора и клиента, располагаются подсистемы ввода/вывода. Подсистемы обработки информации клиентской части и администратора расположены на сервере приложений.Клиент посылает информацию на сервер приложений, где она поступает в подсистему обработки информации клиентской части, а администратор осуществляет мониторинг и управление за поступающей информацией с помощью соответствующей подсистемы обработки информации.
Общая схема взаимодействия клиентов с сервером приложений показана на рис. 4.
Рис. 4
. Схема взаимодействия клиентов, сервера приложений и СУБД.Взаимодействие клиентов с сервером осуществляется следующим образом:
Клиент с помощью подсистемы ввода/вывода запрашивает разрешение на соединение с сервером приложений, посылая соответствующую команду ('R') и идентификатор установки. При этом блокируется семафор, который служит для того, чтобы другой клиент в это время не имел возможности связаться с сервером и вынужден был ждать его освобождения.
Подсистема обработки информации администратора находит свободный канал, открывает новую сессию и посылает подсистеме обработки информации клиентской части идентификатор этой сессии или сообщение о том, что сейчас нет возможности установить соединение. После этого освобождается семафор и сервер становится доступным для других клиентов.
После открытия новой сессии подсистема обработки информации клиентской части записывает информацию о начале работы с клиентом в базу данных.
Когда клиент получает идентификатор сессии, блокируется семафор клиентского канала этой сессии. Семафор остается заблокированным все время взаимодействия клиента с сервером.
Перед тем как послать данные подсистема ввода/вывода клиентской части передает подсистеме обработки информации администратора соответствующую команду ('D') и идентификатор сессии клиента. В случае положительного ответа от подсистемы обработки информации клиентской части клиент посылает данные.
Подсистема обработки информации клиентской части также подключается к этой сессии, принимает и обрабатывает поступающие данные и после обработки каждой порции данных посылает ответное сообщение клиенту, а также записывает допустимые данные в базу данных.
Подсистема обработки информации администратора осуществляет мониторинг обрабатываемых данных и кроме того по требованию администратора посылает некоторые команды клиенту, разблокируя при этом семафор клиентского канала. Также по требованию администратора подсистема обработки информации администратора посылает запросы к серверу баз данных на получение статистической информации. Запрос содержит одну из трех типов команд:
simple
info <номер_устройства>
interval <начальная_дата>,<конечная_дата>
Начальная и конечная даты должны записываться в следующем формате:
yyyy-mm-dd HH:MM:SS.FRC,
где
yyyy - год (4 цифры),mm
- месяц (2 цифры),dd
- день (2 цифры),HH
- часы (2 цифры),MM
- минуты (2 цифры),SS
- секунды (2 цифры),FRC
- доли секунды (3 цифры).После обмена данными подсистема ввода/вывода клиентской части посылает команду на закрытие своей сессии ('U'). Подсистема обработки информации клиентской части закрывает канал для клиентской сессии и делает его свободным для дальнейшего использования, а также заносит в базу данных информацию об окончании работы с клиентом.
Подробнее о типах информации, передаваемой между сторонами распределенной системы, см. на рис. 5.
Рис. 5
. Обмен информацией между клиентами, сервером приложений и СУБД.На рис. 5 использованы следующие условные обозначения:
подсистема1 - подсистема ввода/вывода клиентской части;
подсистема2 - подсистема ввода/вывода администратора;
подсистема3 - подсистема обработки информации клиентской части;
подсистема4 - подсистема обработки информации администратора.
ОПИСАНИЕ ПРОГРАММ. ФУНКЦИИ СЕРВЕРА
Осуществляет подготовку каналов к работе и обработку команд клиентов.
{--------------------------------------------------}
{--------------------------------------------------}
define ClNum integer;
let ClNum = 20;
global define clnums array[ClNum] of character;
global define inchan array[ClNum+1] of integer;
global define outchan array[ClNum+1] of integer;
global define sems array[ClNum+1] of integer;
global define opened integer;
define qsem integer;
define i, j, cnt integer;
define sellst array[2] of integer;
define cmd character;
define uid integer;
define str string;
define f float;
define Master integer;
define nl char;
nl=10;
if opened IS NULL then let opened=0; end if;
if not opened then
let clnums[i] = 'F';
call pipe("pipe"||i) returning inchan[i],outchan[i];
let sems[i] = semget("sem"||i);
end for;
let sems[Master] = semget("semmaster");
let qsem = semget("qmaster");
let j = semlock(sems[Master]);
let opened = 1;
end if;
let sellst[2] = stdin();
while 1
call WinError("select() error:");
return;
end if;
if i == 1 then
call pcon(inchan[Master]);
let cmd = read(inchan[Master],1);
let uid = read(inchan[Master]);
send "Received command '"||cmd||"' from process "||uid,nl;
for i=1 to ClNum
if clnums[i] == 'F' then
exit for;
end if;
end for;
if i > ClNum then
let j=write(outchan[Master],"E");
else
let j=write(outchan[Master],"R"||i);
let clnums[i] = 'T';
end if;
end if;
if clnums[uid] == 'T' then
LET clnums[uid] = 'F';
let j=write(outchan[Master], "U");
else
let j=write(outchan[Master], "E");
end if;
end if;
call pcon(inchan[uid]);
let str = read(inchan[uid]);
send "Session " || uid || " new data: " || str, nl;
call pdcon(inchan[uid]);
call close(inchan[uid]);
call pipe("pipe"||uid) returning inchan[uid], outchan[uid];
end if;
call close(inchan[Master]);
call pipe("master") returning inchan[Master], outchan[Master];
else
exit while;
end if;
end if;
end while;
end function;
Предназначена для управления работой клиента с помощью команд.
{--------------------------------------------------}
{--------------------------------------------------}
define his,hos integer;
define i, cnt, dn integer;
define ctype string;
define dnum, dtbeg, dtend, dt, dt1, sessid string;
define nl character;
let nl = 10;
if NOT ctype IS NULL then
let cnt = 0;
for i=1 to 20 -- ClNum
if clnums[i] == 'T' then
let cnt = cnt+1;
end if;
end for;
send "there are " || cnt || " working devices at this moment."||nl;
return;
end if;
connect to 'astest';
let dnum = strtok(" ");
if dnum IS NULL then
send "Device number is not specified.";
return;
end if;
where time =
( select max(time) from data
where devnum == dnum
and param == -1 );
if SQLCODE < 0 then
send "select error #"||SQLCODE||nl;
disconnect current;
return;
end if;
if SQLCODE == 100 then
send "No information about device "||dnum||nl;
disconnect current;
return;
end if;
let dtbeg = dtbeg CLIPPED;
select time into dtend from data
where time > dtbeg
and data.sid == i
and param == -2;
if SQLCODE < 0 then
send "2-nd select error #"||SQLCODE||nl;
disconnect current;
return;
end if;
if SQLCODE != 100 then
send "Device "||dnum||" is in offline from "||dtend
CLIPPED||"."||nl;
disconnect current;
return;
end if;
select count(*) into cnt from data
where time > dtbeg
and data.sid == i;
send "Already has been inserted "|| cnt ||" parameters."||nl;
disconnect current;
return;
end if;
connect to 'astest';
let cnt = 0;
let dnum = 0;
dtbeg = strtok(",") CLIPPED;
dtend = strtok(",") CLIPPED;
send "Devices worked from "||dtbeg||" till "||dtend||":"||nl;
select time, data.sid, devnum from data
where time <= dtend
and param == -1;
foreach cur into dt, i, sessid
let dt = dt clipped;
select min(time) into dt1 from data
where time > dt
and data.sid == i
and param in (-1,-2)
and time > dtbeg;
if SQLCODE < 0 then
send "3-rd select error #"||SQLCODE||nl;
return;
end if;
if dt1 IS NULL then
select min(time) into dt1 from data
where time > dt
and data.sid == i
and param in (-1,-2);
if dt1 IS NULL then
send "Device ",sessid CLIPPED," with session id ",i,
" has begun work at ",dt," and possibly working now",nl;
let dnum = dnum+1;
end if;
else
" has begun work at ",dt," and finished at ",dt1,nl;
let dnum = dnum+1;
end if;
end foreach;
send "There are " || dnum || " devices was working at this
period."||nl;
disconnect current;
return;
end if;
end if;
send "Error: no process with sid "||sid;
return;
end if;
let hos = outchan[sid];
let i=semunlock(sems[sid]);
call pcon(hos);
let i=write(hos, s);
call pdcon(his);
call close(his);
call pipe("pipe"||sid) returning inchan[sid], outchan[sid];
end function;
Определяет начальные данные для работы клиента с сервером.
{--------------------------------------------------}
{--------------------------------------------------}
global define semm, him, hom, qsem integer;
global define sems integer;
global define min_bound, max_bound float;
define his, hos, i integer;
define answ character;
define sid, cnt, delay integer;
let qsem=semget("qmaster");
call pipe("master") returning him, hom;
let i=semlock(qsem);
let i=semunlock(semm);
let i=write(hom, "R"||getpid());
let answ=read(him,1);
if answ == 'R' then
let sid=read(him);
else
send "Bad answer from server.";
return NULL, NULL, NULL;
end if;
let i=semunlock(qsem);
if SQLCODE != 0 then
send "Connection error #" || SQLCODE;
end if;
select c, d, bmin, bmax into cnt, delay, min_bound, max_bound from schrs
where unit_id == id;
send "Can't retrieve proper information, SQLCODE = "||SQLCODE||".";
return NULL, NULL, NULL;
end if;
if SQLCODE != 0 then
send "Insert error #" || SQLCODE;
end if;
end function;
Оператор DEFINE определяет следующие глобальные переменные:
Осуществляет обработку данных, поступающих от клиента.
{--------------------------------------------------}
{--------------------------------------------------}
define i integer;
define a array[2] of integer;
define his,hos integer;
define datatab string;
let i=semunlock( semm );
call pipe("master") returning him, hom;
let i=write( hom, "D"||sid);
call pipe("pipe"||sid) returning his, hos;
let i=write(hos, data);
call close(his);
let i=semunlock(qsem);
send 1;
let datatab = "data";
INSERT INTO datatab VALUES( CURRENT, data, 0, sid );
if SQLCODE != 0 then
send "Insert error #" || SQLCODE;
return;
end if;
else
send 0;
end if;
let a[2] = stdin();
if semtry(sems) then let i=semlock(sems); end if;
while 1
if i==-1 then exit while; end if;
if i==1 then
call client(sid);
else
if kbhit(a[2]) then
exit while;
end if;
end if;
end while;
end function;
В операторе INSERT переменная datatab содержит имя таблицы "data",
Предназначена для передачи на сервер команды клиента о закрытии клиентской сессии и освобождении клиентского канала.
{--------------------------------------------------}
{--------------------------------------------------}
define i integer;
define s string;
let i=semunlock(semm);
call pipe("master") returning him, hom;
let i=write(hom, "U"||sid);
let s=read(him);
if s != "U" then
send "Unregister error: no session "||sid||".";
else
send "Unregistration successfull.";
end if;
let i=semunlock(qsem);
end function;
ОПИСАНИЕ ПРОГРАММ. ПРОГРАММЫ КЛИЕНТА
Д
анные программы клиента иллюстрируют использование функций взаимодействия с сервером приложений.{--------------------------------------------------}
{--------------------------------------------------}
{
CONNECT* con;
int sc, i, j, inf[3];
char ch;
char *cmd, *buf;
VALUE val;
perror("ConnectTo");
exit(1);
}
perror("SendCmd");
CloseConnect (con);
exit(1);
}
WaitData (con, 1, &buf);
printf ("%s\n", buf);
free (buf);
sc = SendData (con, cmd);
cmd = "call Init(1) returning s, c, d; send s, c, d;";
sc = SendData (con, cmd);
for (i=0;;i++) {
if ((ch = WaitData (con, 3, &buf)) == '\0') break;
if (ch == 'D') {
RetrieveData (buf,&val);
inf[i] = val.val.li;
}
else printf ("%s\n", buf);
free (buf);
}
sprintf (cmd, "call NewData(%d,%g);", inf[0], rnd());
for (i=0; i<inf[1]; i++) {
SendData (con, cmd);
free (cmd);
for (j=0;;j++) {
if ((ch = WaitData (con, 1, &buf)) == '\0') break;
if (ch == 'D') {
RetrieveData (buf, &val);
PrnData (&val); }
free (buf);
}
if (i<inf[1]-1) {
cmd = (char*) malloc (50);
sprintf (cmd, "call NewData(%d,%g);", inf[0], rnd());
sleep(inf[2]/1000);
}
}
if ((i != inf[1]) && (ch == '\0')) printf ("No answer\n");
sprintf (cmd, "call Unreg(%d);", inf[0]);
sc = SendData (con, cmd);
WaitData (con, 1, &buf);
RetrieveData (buf, &val);
PrnData (&val);
free (buf);
free (cmd);
cmd = "end function;";
sc = SendData (con, cmd);
CloseConnect (con);
exit(0);
}
47. set libr to foxgen
ans=""
count = 0
delay = 0
flag = 0
c=waitdata(conid,3,@a)
c=waitdata(conid,3,@a)
n=senddata(conid, "define s,c,d integer;")
n=senddata(conid, "call Init(1) returning s,c,d;")
n=senddata(conid, "send c,d;")
c=waitdata(conid,3,@a)
n=retrdata(a, @count)
c=waitdata(conid,3,@a)
n=retrdata(a, @delay)
i = 0
do while i < count
if rn < 0
rn = 0
endif
if rn > 1000
rn = 1000
endif
wait timeout delay/1000
n=senddata(conid, "call NewData(s,"+str(rn)+");" )
c=waitdata(conid,3,@a)
n=retrdata(a, @flag)
if flag = 1
i = i+1
endif
enddo
c=waitdata(conid,3,@a)
n=sendcmd(conid,"ES")
n=closeconn(conid)
ОПИСАНИЕ ПРОГРАММ. ПРОГРАММА АДМИНИСТРАТОРА
П
рограмма администратора иллюстрирует использование функций взаимодействия с сервером приложений. Подробнее об этих функциях см. соответствующие главы. Данная программа реализована в среде Delphi.interface
uses
SysUtils, Windows, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, Buttons, ExtCtrls, Menus, Connect, ComCtrls;
type
TMainForm = class(TForm)
RichEdit1: TRichEdit;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Sock1: TConnect;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Connect1Error(Sender: TObject; str: String);
procedure Connect1Reply(Sender: TObject; str: String);
procedure Connect1Data(Sender: TObject; val: VALUE);
end;
var
MainForm: TMainForm;
stage, PackLen: integer;
ch : string[1];
st : string;
implementation
uses Unit3;
{$R *.DFM}
begin //simple
Connect1.SendData('CALL Servcmd(1,"Simple");');
Connect1.SendData('CALL Server();');
end;
begin //info
Form3.Show;
st:='CALL Servcmd(1,"info '+vvod+'");';
Connect1.SendData(st);
Connect1.SendData('CALL Server();');
end;
begin //interval
Form3.Show;
st:='CALL Servcmd(1,"interval '+vvod+'");';
Connect1.SendData(st);
Connect1.SendData('CALL Server();');
end;
begin
//программе клиентаForm3.Show;
st:='CALL Servcmd('+vvod+',"Signal");';
Connect1.SendData(st);
Connect1.SendData('CALL Server();');
end;
begin //случае ошибки
RichEdit1.SelAttributes.Color := clRed;
RichEdit1.Lines.Add( str );
end;
begin //подтверждения выполнения команды
RichEdit1.SelAttributes.Color := clBlue;
RichEdit1.Lines.Add( str );
end;
var //данных
s:string;
begin
RichEdit1.SelAttributes.Color := clBlue;
if (val.null = 0) then
case val.vtype of
0: {AS_CHAR }s[1]:=sapis.val.c;
1: {AS_INT }str (sapis.val.li,s);
2: {AS_SMINT }str (sapis.val.si,s);
3: {AS_DOUBLE}str (sapis.val.d,s);
4: {AS_FLOAT }str (sapis.val.f,s);
5: {AS_STRING}s:=sapis.val.p;
end
else
case val.vtype of
0: {AS_CHAR }s:='';
1: {AS_INT }s:='0';
2: {AS_SMINT }s:='0';
3: {AS_DOUBLE}s:='0.0';
4: {AS_FLOAT }s:='0.0';
5: {AS_STRING}s:='';
end;
RichEdit1.Lines.Add( s );
end;
begin
Connect1.SendData('Function;');
Connect1.SendData('CALL Server();');
end.