В статье рассматриваются некоторые проблемы, возникшие при разработке интерфейса доступа к SQL-серверу средствами BDE(Borland Database Engine). Затронут ряд вопросов связаных с существованием различных методов обработки данных в реляционных СУБД и особенностями выполнения транзакций и прямых запросов к SQL- серверу.
 

В последнее время все актуальнее становится вопрос перехода на более мощное программное обеспечение, позволяющее оперировать солидными объемами данных и гарантирующее их целостность при работе в многопользовательском режиме. Все больше становится программистов, имеющих опыт работы с такими СУБД как ORACLE, INGRESS, INFORMIX и т.д. Предлагаема вашему вниманию статья посвящена одному из способов построения пользовательского интерфейса с СУБД ORACLE. В качестве инструментария, обеспечивающего доступ к данным, нами был выбран BDE (Borland Database Engine), ядром которого является IDAPI v.1.2 (Integrated Database Application Program Interface). Выбор этот был продиктован целым рядом причин Основная из них заключалась в необходимости сохранить преемственность при переходе от программного обеспечения, основанного на платформе Paradox 4.5, к новой, более мощной СУБД.

 

Введение в IDAPI


Термин IDAPI (Integrated Database Application Program Interface), являющийся вторым названием BDE (Borland Database Engine), представляет собой программный интерфейс между приложением, с которым "общается" пользователь (или которое разрабатывает программист) и базой данных. В качестве драйвера БД IDAPI может использовать как "родной" (native), т.е. предоставляемый вместе в BDE для определенной платформы (например, Oracle или Informix), так и ODBC - драйвер Microsoft.


Интерфейс IDAPI является средством создани систем "клиент-сервер" , уменьшающих сетевой трафик и позволяющих эффективнее обращаться к удаленным данным. Этот интерфейс предусматривает следующую модель доступа. Когда приложение-клиент обращается к данным посредством одной из функций IDAPI, то сервер передает ему необходимые данные в виде набора записей, полученных из хранимых базовых таблиц. В терминологии BDE такой набор записей называетс dataset.


Обработка dataset, например, редактирование или удаление, является непрямым (non-pass-through) SQL-запросом. Такой запрос выполняется, как правило, средствами IDAPI-объектов, которые стремятся все вносимые изменения сразу передать в хранимые таблицы (и наоборот, все изменения в таблице-источнике отобразить на экране). Только в случае вызова IDAPI-функций, не инкапсулированных в IDAPI -объекты, существует необходимость принудительно синхронизировать отображаемые и хранимые данные.


Обращение к данным может быть прямым (pass-through), т.е. выполняемым операторами SQL (select ... , insert ..., update ..., delete ... и др.). Хотя прямые обращени также осуществляются через IDAPI, они не обязательно связаны с dataset. Для подтверждени (commit) или отката (rollback) сделанных прямыми запросами изменений требуется либо использование механизма транзакций, либо специальные соглашения, устанавливаемые при настройке IDAPI (параметр SQLPASSTHRUMODE).


IDAPI используется такими продуктами Borland, как Delphi и C++, в качестве универсального средства, предоставляющего приложениям доступ к удаленным и локальным данным. Доступ обеспечивается как к файл-ориентированным БД, таким, как dBase, так и к SQL-серве-рам. Главные отличия в функциональности, необходимой для каждой из этих моделей, обсуждаются в статье. Чтобы осуществить прямой вызов IDAPI-функции из программы, необходимо указать файл-источник (в предложении uses для Object Pascal и #include для С), содержащий описание IDAPI. Но в Delphi, например, нет необходимости делать непосредственный вызов IDAPI-функций, т.к. практически для всех нужд разработчика хватает свойств и методов, инкапсулированных в объектах, управляющих работой с данными. По этому же пути идет и автор статьи, который, разрабатывая свои приложения на С++, расширяет один из базовых классов IDAPI методами обработки начала и завершения транзакции.


Средства, подобные IDAPI, как и объекты, позволяющие воспринимать и обрабатывать удаленные данные, есть во всех средствах разработки приложений "клиент-сервер". Так, Oracle Power Objects такие объекты называются контейнерами, в Informix NewEra - супертаблицами, а в Powersoft PowerBuilder - данными в окне (см. статьи "Oracle Power Objects - новый инструмент создания клиентского ПО", Компьютеры+Программы, #6(30), с.48-53; "Клиент-Powerbulder-сервер", Компьютеры+Программы, #7(31), с.54-55). Однако, в отличие от IDAPI, эти средства интегрированы в соответствующую среду разработки и более тесно связаны с окнами, в которых отображаются данные. Кроме того, они ориентированы на работу с собственным SQL-сервером, хотя и предусматривают наличие "родных" драйверов других распространенных БД.


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


BDE хранит информацию о программном окружении клиентской машины в файле конфигурации, называемом idapi.cfg. Для изменени настроек, зафиксированных в этом файле, используется утилита Idapi Configuration Utility. К изменяемым настройкам относятся установленные драйверы баз данных, размер блока БД, число открываемых BDE файлов, формат даты и времени, параметры алиасов BDE. Алиас ("вымышленное имя") представляет собой поименованное табличное пространство, к которому IDAPI-совместимые приложения могут обращаться, указывая лишь им алиаса. Для файл-ориентированной СУБД алиас содержит путь к директории с соответствующими файлами, для SQL-сервера в параметрах конфигурации указывается его сетевой адрес. Алиасы, обеспечивающие доступ к SQL- серверу, имеют такие параметры: путь доступа, драйвер БД, языковый драйвер (langdriver), имя администратора, схема кеширования (количество одновременно кешируемых таблиц), разделение доступа (sqlpassthrumode). Последний параметр подробно обсуждается в статье.

 

Сколько сессий открывает приложение?


Первое, над чем пришлось серьезно поработать, было понимание сессии в IDAPI. При открытии сессии IDAPI на самом деле открывалась не одна, а две сессии. Первая из них предназначена для доступа к БД посредством общепринятого набора API-функций (non-pass-through), как-то открытие таблицы, навигация по записям, добавление и модификация записей и т.д. Вторая сесси открывается для непосредственного (pass-through) выполнения запросов и получения соответствующих курсоров. (Строго говоря, вторая сесси открывается после первого прямого обращения к SQL-серверу.) Оказалось, что способ взаимодействи прямых и "непрямых" запросов регулируется путем установки определенных значений параметра SQLPASSTHRU MODE в утилите IDAPI Сonfiguration Utility. У нас этот параметр был проинициализирован значением по умолчанию для драйвера Borland ORACLE SQL link - NOT SHARED, что и обусловило существование разных сессий для двух разных типов запросов. Допустимы еще два значения - SHARED AUTOCOMMIT и SHARED NOAUTOCOMMIT, которые обеспечивают существование единственной сессии, разделяемой между non-pass-through и pass-through запросами, с той лишь разницей, что первая установка предполагает автоматическое подтверждение изменений после каждого прямого SQL-запроса, а вторая требует явного окончания транзакции (см.рис.1).

 

Рис.1. Утилита конфигурации IDAPI

 

Для работы с SQL-серверами (и только для них) в IDAPI предусмотрены определенные средства для управления транзакциями, понятие которых отсутствует, к примеру, в том же Paradox. Дл выполнения транзакции достаточно зафиксировать ее начало и конец при помощи вызовов функций DbiBeginTran и DbiEndTran соответственно. Для всех манипуляций с данными, выполняемых API-функциями вне явного задания транзакции, COMMIT (подтверждение изменений) делаетс автоматически.


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


Во-первых, если вы пришли к решению о необходимости отдельных сессий для разного рода запросов, то надо помнить, что изменения в одной из сессий не отслеживаются во второй. Так, допустим, что вам необходимо занести в БД две записи, логически связанные между собой. Пускай одна из записей будет заноситься при помощи стандартного набора API- функций, а вторая - посредством прямого выполнения SQL-запроса. Пускай также одна из записей приводит к нарушению целостности БД, а вторая полностью корректна. Тогда в результате занесени нарушающей целостность БД записи в соответствующей сессии произойдет откат, в то время как вторая запись будет благополучно занесена.


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


Существует ряд особенностей, на которые хотелось бы обратить внимание. Например, если между вызовами функций начала и окончани транзакции произойдет прямой SQL-запрос, содержащий допустимое DDL-предложение (Data Definition Language statement), в котором встретится оператор commit или rollback, то транзакция завершится. Это стандартна стратегия SQL-серверов. В случае установки SHARED NOAUTOCOMMIT, если явно не задать транзакцию, то транзакция завершится не только тогда, когда допустимое DDL-предложение будет содержать commit или rollback, но и в результате вызова одной из IDAPI-функций. Это вызвано тем, что любая операция, выполняемая посредством такой API-функции, подтверждается автоматически. Следовательно, будут подтверждены и все предшествовавшие этой API-функции запросы обоих типов.


В-третьих, SQL link у конечного пользователя может быть сконфигурирован не так, как предполагает реализованная вами стратеги управления транзакциями. Тогда, если ваше приложение рассчитано, к примеру, на стратегию SHARED NOAUTOCOMMIT, а у пользователя установлено SHARED AUTOCOMMIT, то любые ваши прямые запросы будут сразу же "закомичены", не взирая на все явные вызовы commit и rollback.

 

Одна сессия - хорошо, а две - лучше


Все эти особенности накладывают определенные ограничения на режим разработки приложений и заставляют достаточно строго отслеживать моменты, связанные с транзакциями. Мы нашли простой, но достаточно эффективный выход из сложившейся ситуации. Мы предположили, что две сессии всегда лучше одной. Это спорное утверждение, но с практической точки зрения оно кажется верным. Так, независимость сессий дает то преимущество, что целые блоки манипуляций данными (например, те, которые не затрагивают интерфейс пользователя) можно выполнять исключительно прямыми SQL-запроса-ми. Но все же для полной уверенности в правильном исполнении ваших команд перед запуском приложения необходимо сконфигурировать SQL link.


Для тех ситуаций, когда удобно использовать разнородные типы запросов в пределах одной транзакции, нами было принято простое решение. Поскольку разработка проекта велась на Borland C 4.5 и большинство функций IDAPI были "зашиты" в классы, то и для отслеживани транзакций были созданы соответствующие методы. Суть решения проблемы заключается в объединении начала и конца транзакций обеих сессий:

 

//Функция установки начала транзакции

void OALSession::SetBeginTran(BOOL commit) {
//если уже была открыта
if(Trans) { if(commit) Commit();else RollBack(); }
Trans = TRUE;
return errMsg(DbiBeginTran(hDb,xilREADCOMMITTED,&hXact));
}

//Функция завершения транзакции по commit (аналогочно для rollback)

void OALSession::Commit(void) {
if(!Trans) return;
hDBICur cursor;
errMsg(DbiQExecDirect(hDb, qrylangSQL, "COMMIT",&cursor));
Trans = FALSE;
errMsg(DbiEndTran(hDb, hXact, xendCOMMIT));
}

 

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


Проблемы, которые могут возникнуть при использовании IDAPI


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


Прежде всего, следует заметить, что при обработке запросов ORACLE используется часть памяти, называемая временными сегментами, для хранени промежуточных результатов выполнени SQL-опера-торов. Наиболее характерный пример - запрос с использованием сортировки: SELECT ... ORDER BY..., SELECT ... GROUP BY... и т.д. Чаще всего под временные сегменты выделяется специальное табличное пространство. Допустим, что при помощи функций BDE вы находите определенную запись таблицы по ключу. В интерпретации IDAPI это будет выглядеть примерно следующим образом:

 

DbiSetToKey(cursor, keySEARCHEQ, FALSE, 0,0, RecBuf);

 

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

 

DbiGetPriorRecord(cursor, dbiNOLOCK, RecBuf, NULL);

 

К сожалению подобная неосторожность может привести к генерации примерно следующего SQL-запроса:

 

SELECT "A" ,"B" , "C"
FROM "TEST_TABLE"
WHERE (("A" = :1 AND "B">:2) OR ("A"> :3))
ORDER BY "A" ASC , "B" ASC

 

где "A" и "B" - проиндексированные пол таблицы, а :1. :2. :3 - переменные ORACLE, хранящие в данный момент значения полей ключа поиска. "Вычислить" это можно посредством инспекции словаря БД сразу же после попытки перехода на соседнюю логическую запись примерно следующим SQL-запросом:

 

SELECT PIECE, SQL_TEXT
FROM V$SQLTEXT
WHERE ADDRESS IN (SELECT ADDRESS
FROM V$OPEN_CURSOR
WHERE USER_NAME = ‘SCOT')
ORDER BY HASH_VALUE, PIECE;

 

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

 

Рис.2. Сообщение о нехватке ресурсов

 

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


Разумеется, нам пришлось столкнутся не только с крупными неприятностями, но и с рядом мелких неудобств. Например, значительная часть проекта была написана на процедурном расширении SQL - PL/SQL. Кроме того, что вызывать процедуры и функции при помощи BDE приходится несколько необычным образом:

 

DbiQExecDirect (DbHandle, SQLLang, "begin test_proc; end;", cursor);

 

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


Удручает и то, что не реализована така "хитовая" возможность, как вложенный запрос (nested query), и это вызывает трудности при задании даже такого элементарного SQL-запроса:

 

SELECT *
FROM "TEST_1.DB"
WHERE A IN (SELECT B
FROM "TEST_2.DB");

 

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

 

 

Рис.3. Получение информации о вычислимом поле

 

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

 

SAVEPOINT label1;
INSERT INTO test VALUES(1, 2, 3);
SAVEPOINT label2;
INSERT INTO test VALUES(1, 2, 3);
ROLLBACK TO label2;

 

В заключении заметим, что сомнительны заявки о возможности существовани универсальной системы функций для баз данных, у которых даже терминология существенно разнится. Достаточно упомянуть, что целый ряд таких функций снабжен примечаниями типа: "This function is not supported for dBASE tables" (возможные варианты - SQL, Paradox и т.д.) В основном это конечно функции обеспечивающие довольно специфические возможности, как например DbiAddPassword, DbiGetDirectory и т.п. (не для SQL) или уже упоминавшиеся DbiBeginTran и DbiEndTran (только для SQL), но тем не менее их наличие не позволяет создавать универсальные приложения.


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

  • Механическое применение приложений, ориентированных на определенную БД, к разнородным базам данных ведет если не к полному краху, то к существенному снижению эффективности.
  • Использование подобных систем целесообразно, видимо, лишь для написани интерфейсной части приложения, а все связанное с серьезной обработкой данных должно быть вынесено в отдельные модули, разрабатываемые "родными" средствами БД. Кроме того, сама интерфейсная часть также должна быть построена с учетом особенностей идеологии обработки данных конкретной БД.
2005.02.04
19.03.2009
В IV квартале 2008 г. украинский рынок серверов по сравнению с аналогичным периодом прошлого года сократился в денежном выражении на 34% – до $30 млн (в ценах для конечных пользователей), а за весь календарный год – более чем на 5%, до 132 млн долл.


12.03.2009
4 марта в Киеве компания Telco провела конференцию "Инновационные телекоммуникации", посвященную новым эффективным телекоммуникационным технологиям для решения задач современного бизнеса.


05.03.2009
25 февраля в Киеве компания IBM, при информационной поддержке "1С" и Canonical, провела конференцию "Как сохранить деньги в условиях кризиса?"


26.02.2009
18-19 февраля в Киеве прошел юбилейный съезд ИТ-директоров Украины. Участниками данного мероприятия стали ИТ-директора, ИТ-менеджеры, поставщики ИТ-решений из Киева, Николаева, Днепропетровска, Чернигова и других городов Украины...


19.02.2009
10 февраля в Киеве состоялась пресс-конференция, посвященная итогам деятельности компании "DiaWest – Комп’ютерний світ" в 2008 году.


12.02.2009
С 5 февраля 2009 г. в Киеве начали работу учебные курсы по использованию услуг "электронного предприятия/ учреждения" на базе сети информационно-маркетинговых центров (ИМЦ).


04.02.2009
29 января 2009 года в редакции еженедельника "Computer World/Украина" состоялось награждение победителей акции "Оформи подписку – получи приз!".


29.01.2009
22 января в Киеве компания "МУК" и представительство компании Cisco в Украине провели семинар для партнеров "Обзор продуктов и решений Cisco Small Business"

 

 
 
Copyright © 1997-2008 ИД "Комиздат".