    26 ГЛАВА 

    Borland Database Engine 

    Система Borland Database Engine (BDE) представляет собой набор библиотек 
динамической компоновки (DLL — Dynamic Link Library), посредством которых 
осуществляется доступ к базам данных в Delphi. BDE используется не только 
Delphi, но и рядом других программных продуктов фирмы Borland (например, 
Paradox для Windows, Visual dBASE, Borland C++ Builder и др.), в которых 
применяется также старое название BDE — IDAPI (Independent Database 
Application Programming Interface — независимый программный интерфейс 
приложений баз данных). Вначале фирма Borland выпустила IDAPI как систему 
взаимодействия между базами данных, но впоследствии система ODBC фирмы 
Microsoft затмила ее собой в этой роли. 
    Практически все функциональные возможности BDE инкапсулированы в 
компонентах баз данных Delphi, поэтому необходимость непосредственного 
обращения к BDE возникает редко, а то и вообще не возникает. Средства Delphi 
несравнимо проще в использовании и намного более надежны, чем прямые обращения 
к BDE. К тому же, вопреки возможным ожиданиям пользователей, от прямого 
обращения к BDE нельзя получить большой выигрыш в быстродействии. 
    Зачем же тогда в этой книге глава, посвященная BDE? В этой главе 
рассказывается о внутреннем устройстве системы, знание которого облегчает 
понимание функционирования основных средств Delphi. Кроме того, вы будете 
знать, как действовать в тех редких случаях, когда приходится обращаться за 
помощью непосредственно к BDE по причине отсутствия необходимых возможностей в 
компонентах баз данных Delphi. 
    При прямом обращении к BDE необходимо иметь в виду следующее. Во-первых, в 
этом случае отсутствует система обеспечения безопасности в виде исключительных 
ситуаций Delphi VCL, поэтому необходимо самостоятельно проверять коды возврата 
из функций BDE и соответственно на них реагировать. Во-вторых, поскольку 
компоненты баз данных Delphi не осведомлены о низкоуровневых операциях 
ввода-вывода, эти операции необходимо отдельно синхронизировать с 
компонентами. Все зависит от ситуации — иногда проблема решается вызовом 
метода Refresh, а в других случаях бывает необходимо закрыть и снова открыть 
компонент семейства DataSet. 
    В этой главе система BDE обсуждается в качестве средства для расширения 
возможностей доступа к базам данных, имеющимся в VCL Delphi, а не в качестве 
альтернативы Delphi. Вы познакомитесь с ключевыми структурами BDE, узнаете о 
способах использования функций BDE, а также о совместной работе компонентов 
Delphi и функций программного интерфейса BDE. 

    Сравнительная характеристика систем BDE и ODBC 

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

    496 Часть IV. Темы повышенной сложности 

    то и не поддерживаются тем сонмом средств, которые должны, по идее, 
обеспечивать полную совместимость ODBC со всеми СУБД. 
    Для BDE все перечисленное не характерно. Как правило, ее драйверы SQL Link 
обеспечивают полную совместимость с соответствующими СУБД, и эта совместимость 
остается в силе и для компонентов баз данных Delphi. Например, SQL-серверы баз 
данных предусматривают механизмы контроля транзакций, которые позволяют 
группировать вносимые в базы данных изменения так, чтобы они применялись или 
отменялись все вместе. Синтаксические конструкции языка SQL, необходимые для 
инициирования группы транзакций, во всех серверах различны, а локальные 
таблицы вообще не поддерживают систему транзакций. Несмотря на различия в 
синтаксисе SQL разных поставщиков и отсутствие поддержки транзакций в 
локальных таблицах, в API BDE имеется функция DbiBeginTran, которая работает с 
любыми СУБД. Эту функцию можно использовать даже для локальных таблиц — в этом 
случае BDE использует собственные механизмы контроля транзакций. Данный вызов 
API замаскирован в методе Delphi BeginTransaction компонента TDatabase, так 
что транзакц ии можно инициировать, не беспокоясь о синтаксисе языка SQL. 
Кроме того, можно использовать транзакции в локальных таблицах, даже несмотря 
на то, что для самих таблиц такого понятия не существует. Итак, там, где 
соответствующий драйвер ODBC может не поддерживать транзакции в локальных 
таблицах, BDE предусматривает единый вызов API для всех поддерживаемых СУБД. 

    Архитектура 

    Архитектура BDE основана на системе драйверов. Это означает, что для 
обращения к каждой СУБД необходим отдельный драйвер. Иногда один драйвер может 
обслуживать несколько различных СУБД (например, один драйвер предусмотрен для 
всех платформ dBASE, включая dBASE III, IV, V, FoxPro и Clipper). 
    Чтобы нагляднее представить архитектуру BDE, рассмотрим последовательность 
событий, происходящих при обращении к объекту базы данных из Delphi. 
    1.	Приложение выполняет вызов компонента базы данных Delphi. 
    2.	Компонент вызывает BDE. 
    3.	BDE вызывает локальную базу данных или драйвер SQL Links. 
    4.	Если используется локальный драйвер, то он обращается к таблицам базы данных. 
    В противном случае, т.е. если используется драйвер SQL Links, он вызывает 
драйвер клиента, поставляемый в составе СУБД. 
    BDE — объектно-ориентированная система. Таким образом, она проста в 
использовании и легко расширяется. Если необходимо обратиться к СУБД, для 
которой в BDE нет готовой поддержки, то достаточно установить для этой СУБД 
нужный драйвер BDE или ODBC. Практически невозможно представить себе ситуацию, 
в которой для поддержки какой-нибудь новой платформы СУБД пришлось бы 
переделывать саму системы BDE. 
    BDE имеет архитектуру типа клиент/сервер. Это означает, что в ней 
поддерживаются все современные средства и возможности, которых пользователи 
ожидают от таких СУБД: обработка транзакций, модифицируемые курсоры, хранимые 
процедуры и т.д. Вместо применения для поддержки отношений клиент/сервер 
метода "наименьшего общего делителя" в BDE предусмотрен интерфейс 
программирования приложений (API), представляющий собой надмножество функций 
всех поддерживаемых СУБД. Поскольку в BDE драйверы и API разграничены между 
собой, переход от одной платформы к другой происходит очень легко. Для 
масштабирования баз данных от локальных таблиц к системам типа клиент/сервер 
иногда достаточно всего лишь таким образом изменить псевдоним базы данных, 
используемый в приложении, чтобы он указывал на новую СУБД. 

    Совместно используемые службы 

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

    Глава 26. Borland Database Engine 497 

    Службы операционной системы 

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

    Диспетчер буферизации 

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

    Диспетчер памяти 

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

    Кэширование объектов BLOB 

    Для облегчения доступа к большим объектам в бинарном формате (binary large 
objects — BLOB) в BDE предусмотрено их кэширование. Благодаря этому 
программисты освобождаются от забот о самостоятельном управлении кэшированием. 
Кроме того, обеспечивается прямой доступ к объектам BLOB, чтобы пользователям 
BDE не приходилось переписывать содержимое этих объектов во внешний файл. 
Прямой доступ поддерживается этим способом даже в тех СУБД, в которых он сам 
по себе не предусмотрен. 

    Сортировщик 

    В BDE имеется скоростной сортировщик, который используется обработчиком 
запросов, а также драйверами dBASE и Paradox. У фирмы Borland имеются два 
авторских свидетельства на его архитектуру. 

    Обработчик запросов 

    Обработчик запросов BDE — это устойчивая полнофункциональная система 
обработки запросов, которая быстро генерирует сложные наборы данных. Она 
поддерживает метод запроса по образцу (Query By Example — QBE) и прямой 
SQL-метод обращения к базам данных через любой драйвер. Например, для 
выполнения запроса к таблицам dBASE можно использовать язык SQL, хотя эти 
таблицы являются локальными и не имеют встроенной поддержки SQL. 

    Генератор SQL 

    Как уже упоминалось, BDE предоставляет возможность обращаться к базам 
данных с запросами как с помощью метода QBE, который приобрел широкое 
распространение благодаря СУБД Paradox, так и с помощью традиционных 
операторов SQL. Перед пересылкой QBE-запроса на SQL-сервер система BDE 
преобразует его в операторы языка SQL. 

    Служба Restructure 

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

    498	Часть IV. Темы повышенной сложности 

    Служба преобразования данных 

    В BDE имеется служба преобразования данных из одного формата в другой. 
Благодаря этому функции и службы BDE могут выполнять операции по обмену данных 
между базами данных различных СУБД. При выполнении преобразования данных эта 
служба автоматически выбирает оптимальный метод преобразования. 

    Пакетная обработка таблиц 

    BDE располагает мощной службой слияния таблиц и перемещения данных между 
таблицами. Эта служба является основой компонента TBatchMove и поставляемого 
вместе с Delphi средства Data Pump. 

    Размещение таблиц в памяти 

    Как и многие СУБД архитектуры клиент/сервер, BDE поддерживает размещение 
временных таблиц прямо в оперативной памяти компьютера. Это ускоряет доступ к 
данным и их сортировку, а также образует необходимую основу для кэширования модификаций. 

    Связанные курсоры 

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

    Службы драйверов SQL 

    Для драйверов SQL в BDE имеется целый набор мини-служб. Среди них — 
преобразование команд перемещения по таблице в операторы SQL для единообразия 
доступа к локальным и удаленным таблицам, службы словаря данных и каталога 
объектов, а также служба манипулирования кэшированными объектами BLOB. 

    Системный диспетчер 

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

    Диспетчер конфигурации 

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

    Драйверы языковой поддержки 

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

    Интерфейс прикладных программ BDE 

    Интерфейс прикладных программ (API — Application Programming Interface) 
BDE основан на системе курсоров и образует единый унифицированный набор 
функций как для локальных (ISAM), так и для удаленных (SQL) источников данных. 
Каждый тип СУБД оказал определенное влияние на API, отчего в итоге выиграли 
все. Например, хотя в существующих СУБД двустороннее перемещение по объектам баз 

    Глава 26. Borland Database Engine 499 

    данных серверов SQL не поддерживается или поддерживается неудачно, в 
Delphi имеются средства перемещения по удаленным наборам данных как вперед, 
так и назад. Этого можно было бы ожидать от таблиц локальных баз данных, но 
никак не от удаленных. Подобным же образом, несмотря на то, что в локальных 
форматах dBASE и Paradox нет встроенной поддержки транзакций, BDE создает для 
них механизм управления транзакциями с теми же функции API, что и в СУБД, в 
которых предусмотрено управление транзакциями. 
    Ввиду учета в API BDE индивидуальных особенностей каждой СУБД можно 
сказать, что этот интерфейс поймал обоих зайцев, за которыми погнался. 
Например, в настоящее время в драйверах dBASE поддерживаются индексы Clipper, 
в драйверах Paradox — первичные индексы, а хранимые процедуры поддерживаются в 
драйверах тех СУБД типа клиент/сервер, в которых они предусмотрены. 

    Минимальная функциональность 

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

    Ключевые структуры BDE 

    Для разработки приложений, взаимодействующих с BDE, полезно знать ключевые 
структуры данных, используемые этой системой. Эти структуры перечислены в 
табл. 26.1. 

    Таблица 26.1. Ключевые структуры данных BDE 

    Структура				Описание 

BATTblDesc                      	Дескриптор пакетной обработки таблиц 
CANHdr                             	Заголовок класса узла фильтрации 
CBPROGRESSDesc       			Процедура обратного вызова для индикатора выполнения процесса 
RESTcbDesc                    	Процедура обратного вызова для реструктуризации таблицы 
CFGDesc                          	Дескриптор конфигурации 
CLlENTinfo                        	Информация о приложении-клиенте 
CRTblDesc                        	Дескриптор атрибутов таблицы 
CURProps                          	Дескриптор свойств курсора 
DBDesc                              Дескриптор базы данных 
DBlEnumFld                       	Поле перечислимого типа 
DBlEnv                              Дескриптор среды BDE 
DBlErrinfо					Дескриптор информации об ошибках 
DBIQryProgress     			Индикатор состояния запроса 
DRVType                           	Дескриптор драйвера 
FlLEDesc                            Дескриптор файла 
FlLTERlnfo                     	Дескриптор фильтра 
FLDDesc             			Дескриптор поля 
FLDType               			Дескриптор типа поля 

    500	Часть IV. Темы повышенной сложности 

    Окончание табл. 26.1. Структура	Описание 
    FMLDesc	Дескриптор	драйвера 
    FMTBcd	Двоично-десятичный формат 
    FMTDate	Формат даты 
    FMTNumber	Числовой формат 
    FMTTime	Формат времени 
    DbiFUNCArgDesc	Дескриптор	аргумента функции удаленного источника данных 
    DbiFUNCDesc	Дескриптор	функции удаленного источника данных 
    IDXDesc	Дескриптор	индекса 
    IDXType	Дескриптор	типа индекса 
    LDDesc	Дескриптор	языкового драйвера 
    LOCKDesc	Дескриптор	блокировки 
    RECProps	Дескриптор	свойств записи 
    RINTDesc	Дескриптор	ссылочной целостности 
    SECDesc	Дескриптор	защиты 
    SESInfo	Дескриптор	информации о сеансе 
    SPDesc	Дескриптор	стандартной процедуры 
    SPParamDesc	Дескриптор	параметров стандартной процедуры 
    SYSConfig	Дескриптор	системной конфигурации 
    SYSInfo	Дескриптор	состояния системы BDE 
    SYSVersion	Дескриптор	версии системы BDE 
    TBLBaseDesc	Дескриптор	основной информации о таблице 
    TblExtDesc	Дескриптор	дополнительной информации о таблице 
    TBLFullDesc	Дескриптор	полной информации о таблице 
    TBLType	Дескриптор	вместимости таблицы 
    USERDesc	Дескриптор	пользователя 
    VCHKDesc	Дескриптор	проверки допустимости 
    Xlnfo	Дескриптор	транзакции 

    Кроме этих структур, в BDE определены также объекты, перечисленные в табл. 26.2. 
    
    Таблица 26.2. Объекты, определенные в BDE 
    
    
    Объект	Тип	Описание 
    hDBICur	hDBIObj	Дескриптор курсора 
    hDBIDb	hDBIObj	Дескриптор базы данных 
    hDBIObj	UINT32	Дескриптор неопределенного	объекта 
    hDBIQry	hDBIObj	Дескриптор запроса 
    hDBISes	hDBIObj	Дескриптор сеанса 
    hDBIStmt	hDBIObj	Дескриптор оператора нового	запроса 

    Глава 26. Borland Database Engine 501 

    Окончание табл. 26.2. 

    Объект	Тип	Описание 

    hDBIXact	UINT32	Дескриптор транзакции 
    hDBIXlt	hDBIObj	Дескриптор преобразования 
    phDBICfg	^hDBICfg	Указатель на дескриптор конфигурации 
    phDBICur	ЛпОВ1Сиг	Указатель на дескриптор курсора 
    phDBIDb	"hDBIDb	Указатель на дескриптор базы данных 
    phDBIObj	^hDBIObj	Указатель на дескриптор неопределенного объекта 
    phDBIQry	AhDBIQry	Указатель на дескриптор запроса 
    phDBISes	^hDBISes	Указатель на дескриптор сеанса 
    Phdbistmt	~hdbistmt	Указатель на дескриптор оператора 
    phDBIXact	AhDBIXact	Указатель на дескриптор транзакции 
    phDBIXlt	"hDBIXlt	Указатель на дескриптор преобразования 

    В приложениях чаще всего используются переменные hDBlDb и hDBlCur. 
Переменная hDBiob возвращается при открытии базы данных, а переменная hDBICur 
— при открытии таблицы. Эти переменные соответствуют свойствам DBHandle и 
Handle объектов TDBDataSet и TBDEDataSet. 

    Приложение с прямым обращением к системе ВОЕ 

    Как уже говорилось, практически никогда не возникает необходимость в 
разработке целой программы, построенной на вызовах функций BDE, а не на 
использовании компонентов баз данных Delphi. Тем не менее полезно знать, как 
такие программы работают и какие ресурсы им нужны. Это помогает понять процесс 
обращения к базам данных в Delphi. Общая схема работы приложения, 
использующего только вызовы функций BDE, состоит из следующих двенадцати этапов. 
    1.    Инициализация системы BDE. 
    2.   Установка уровня отладки. 
    3.    Открытие базы данных. 
    4.    Задание рабочего каталога. 
    5.    Задание временного каталога. 
    6.    Создание курсора путем открытия таблицы. 
    7.   Чтение свойств таблицы. 
    8.   Распределение памяти под буфер записи. 
    9.    Перемещение курсора на нужную запись. 
    10. Чтение записи, на которую установлен курсор. 
    11. Чтение нужного поля из записи. 
    12. Освобождение выделенной памяти. 
    Листинг 26.1 содержит модуль, в котором реализованы все двенадцать этапов. 
Я взял за основу демонстрационное приложение из справочной системы BDE, 
написанное на языке С, и перевел его на Object Pascal, а затем преобразовал в 
обработчик события OnClick компонента Button. 

502 Часть IV. Темы повышенной сложности 


    Листинг 26.1. Демонстрационное приложение из справочной системы ВОЕ, 
написанное на языке Object Pascal в виде обработчика события OnClick 
    
    
    { {      Программа демонстрации методов использования ВОЕ { {          
Copyright (с) 1996 Borland International {	****} } } } } } 
    {	**** ) 
    Перевод на Object Pascal -- Кена Хендерсона. } unit BDETEMP; 
    interface 
    uses 
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 
StdCtrls, ToolWin, ComCtrls, BDE; 
    type 
    TForml = class(TForm) 
    Buttonl: TButton; 
    procedure ButtonlClick(Sender: TObject); private 
    { Private declarations } public 
    { Public declarations } end; 
    var 
    Forml: TForml; 
    implementation {$R *.DFM} 
    procedure TForml.ButtonlClick(Sender: TObject); var 
    hDb :  hDBIDb;	// Дескриптор базы данных. 
    hCur : hDBICur;	// Дескриптор курсора таблицы. 
    szTblName : String; 
    szTblType : String; 
    CursorProps : CURProps;	// Свойства курсора. 
    pRecBuf : pByte;	// Указатель на буфер записи. 
    EmpNo : Integer; 
    isBlank : BOOL; function Chk(ErrorValue : DBIResult) : DBIResult; 
    var 
    dbi_status : String; 
    dbi_string : String; 
    Errlnfo  : DBIErrlnfo; 
    count : Integer; begin 
    dbi_status:=''; 
    dbi_string:=''; 
    if (ErrorValue <> DBIERR_NONE) then begin DbiGetErrorInfo(TRUE, Errlnfo); 
    if (Errlnfo.iError = ErrorValue) then begin dbi_status:='  ERROR '+Errlnfo.szErrCode; 
    With Errlnfo do 

    Глава 26. Borland Database Engine 503 

    for count:=low(szContext) to high(szContext) do 
    if (strcomp(Errlnfo.szContext[count], '') <> 0) then 
    dbi_status :=  dbi_status+'    '+Errlnfo.szContext[count] end else begin 
    SetLength(dbi_string, DBIMAXMSGLEN); DbiGetErrorString(ErrorValue, 
PChar(dbi_string)); dbi_status := '  ERROR '+dbi_string; end; MessageBox(0, PChar(dbi_status), 
    'BDE Error', MB_OK or MB_ICONEXCLAMATION); end; 
    result:=ErrorValue; end; begin 
    hDB := nil; 
    hCur := nil; 
    ShowMessage('Initialize the engine.'); 
    Chk(DbiInit (nil));	// Этап 1. 
    ShowMessage('Set up the debug layer.'); 
    DbiDebugLayerOptions(DEBUGON or OUTPUTTOFILE or FLUSHEVERYOP, 
'TRACE.TXT');	// Этап 2. 
    ShowMessage('Open a database.'); 
    Chk(DbiOpenDatabase(        // Этап 3. 
    nil,	// Имя базы данных (БД). nil - имя STANDARD. 
    nil,	// Тип БД. nil - тип STANDARD. 
    dbiREADWRITE,	// Режим, в котором открывается БД. 
    dbiOPENSHARED,	// Режим совместного использования БД. 
    nil,	// Пароль. Для БД STANDARD не нужен. 
    О,	// Количество необязательных параметров. 
    nil,	// Описание необязательных параметров. 
    nil,	// Значения необязательных параметров. 
    hDb));	// Дескриптор базы данных. 
    ShowMessage('Set the work directory.'); Chk(DbiSetDirectory(        // 
Этап 4. 
    hDb,	// Дескриптор модифицируемой БД. 
    'D:\Program Files\Borland\Delphi 3\Demos\Data')); 
    // Выбор рабочего каталога. 
    ShowMessage('Set the temporary directory.'); Chk(DbiSetPrivateDir(       
// Этап 5. 
    'D:\WINDOWS\TEMP'));    // Выбор каталога для временных файлов. 
    szTblName:='EMPLOYEE'; 
    szTblType:=szPARADOX; 
    ShowMessage('Create a cursor by opening a table.'); 
    Chk(DbiOpenTable(	// Этап 6. 
    hDb,	// Дескриптор стандартной БД.     *' 
    PChar(szTblName),       // Имя таблицы БД. 
    PChar(szTblType),      // Тип таблицы (только для локальных таблиц). 
    nil,	// Имя индекса (необязательно). 
    nil,	// Имя тэга индекса (обязательно только для dBASE). 
    О,	// Тип индекса (0 - первичный). 
    dbiREADWRITE,	// Режим, в котором открывается таблица. 
    dbiOPENSHARED,	// Режим совместного использования таблицы. 
    xltFIELD,	// Режим преобразования (как правило, xltFIELD). 
    FALSE,	// Поддержка только однонаправленного курсора. 
    nil,	// Необязательные параметры. 
    hCur));	// Дескриптор курсора. 
    ShowMessage('Retrieve the	table''s properties.'); 
    ChkfDbiGetCursorProps(	// Этап 7. 
    hCur,	// Дескриптор курсора. 

    504 Часть IV. Темы повышенной сложности 

    CursorProps));         // Свойства курсора таблицы. 
    ShowMessage('Allocate a record buffer area.'); GetMem(pRecBuf, 
CursorProps.iRecBufSize * 
    sizeOf(BYTE));         // Этап 8. if (pRecBuf = nil) then 
    ShowMessage('Error allocating buffer.') else begin 
    ShowMessage('Set cursor to the crack before the first record.'); 
Chk(DbiSetToBegin(hCur)); // Этап 9. 
    // Установка курсора перед первой // записью таблицы. 
    ShowMessage('Retrieve the	record from the cursor.'); 
    Chk(DbiGetNextRecordf	// Этап 10. 
    hCur,	// Дескриптор курсора. 
    dbiNOLOCK,	// Тип блокировки. 
    pRecBuf,	// Буфер для хранения полученной записи. 
    nil));	// Дескриптор структуры, описывающей 
    // свойства записи. В рассматриваемом 	// примере не нужен. 
    ShowMessage('Retrieve the	field from the record.'); 
    Chk(DbiGetField(	// Этап 11. 
    hCur,	// Дескриптор курсора. 
    1,	// Номер поля. 
    pRecBuf,	// Буфер, в котором хранится запись. 
    @EmpNo,	// Переменная, представляющая значение поля. 
    isBlank)) ;	// Флаг, показывающий, является ли поле пустым. 
    ShowMessage('The retrieved field value is '+ 
    IntToStr(EmpNo)+'.'); end; 
    ShowMessage('Free  all  allocation.'); if   (pRecBuf  <>  nil)   then	//   
Этап   12. 
    FreeMem(pRecBuf);	//   Освобождение  памяти  для  буфера   записи, if   
(hCur <> nil)     then 
    Chk(DbiCloseCursor(hCur));//   Закрытие   курсора. DbiExit;	//  
Деинициализация  BDE. end; end.                                                
                                                     * 
    Как видно, этот программный фрагмент асссоциирован с событием OnClick 
кнопки формы Delphi. Вы можете вставить этот фрагмент в свой обработчик этого 
события и посмотреть, как он работает. Эта программа обращается к таблице 
EMPLOYEE, находящейся в каталоге C:\Program Files\Borland\Delphi 3\Demos\Data, 
поэтому для ее корректной работы такая таблица должна существовать. 
    Обратите внимание на использование модуля BDE в операторе Uses. Модуль BDE 
содержит все процедуры и типы данных для системы BDE. Этот модуль необходимо 
включать в оператор Uses всякий раз, когда программа должна напрямую 
обращаться к BDE. 
    „Хотя большинство разработчиков приложений Delphi никогда не программирует 
обращение к базам данных целиком на основе вызовов API BDE, приведенная выше 
программа может служить шаблоном для выполнения такого рода работы, если в ней 
возникнет необходимость. 

    Обращение к BDE из приложений Delphi 

    Наиболее вероятным сценарием использования BDE является разработка 
приложения, в котором практически весь доступ к базе данных выполняется с 
использованием встроенных компонентов Delphi, но для некоторых специальных 
функций применяются прямые вызовы BDE. Наглядной иллюстрацией такой 
организации программы является компонент LiveQuery, подробно описанный в главе 
27, "Разработка компонентов баз данных". Он использует функцию BDE 
DBiQExecDirect для выполнения запроса при обработке события BeforeOpen потомка 
компонента ТТаЫе. Листинг 26.2 содержит исходный код компонента LiveQuery. 

    Глава 26. Borland Database Engine	505 

    ЛИСТИНГ 26.2. Компонент LiveQuery, ИСПОЛЬЗУЮЩИЙ функцию BDE   DBiQExecDirect 

    unit Liveqry; interface 
    uses 
    SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, 
Forms, Dialogs, Db, DBTables, BDE; 
    const 
    DEFAULTCREATEVIEWSQL = 'CREATE VIEW %s AS '; DEFAULTDROPVIEWSQL = 'DROP 
VIEW %s'; DEFAULTTABLENAMEFORMAT = 'TV%s'; 
    type 
    TLiveQuery = class (TTable) private 
    { Private declarations } 
    FCreateViewSQL : String; 
    FDropViewSQL : String; 
    FTableNameFormat : TFileName; 
    FSQL : TStrings; 
    procedure SetQuery(Value: TStrings); protected 
    { Protected declarations } 
    procedure CreateTemporaryView; 
    procedure DropTemporaryView; 
    procedure DoBeforeOpen; override; 
    procedure DoAfterClose; override; public 
    { Public declarations } 
    constructor Create(AOwner: TComponent); override; 
    destructor Destroy; override; published 
    { Published declarations } 
    property CreateViewSQL : String read FCreateViewSQL write FCreateViewSQL; 
    property DropViewSQL : String read  FDropViewSQL write FDropViewSQL; 
    property SQL : TStrings read FSQL write SetQuery; 
    property TableNameFormat : 
    TFileName read FTableNameFormat write FTableNameFormat; end; 
    procedure Register; implementation 
    constructor TLiveQuery.Create(AOwner: TComponent); begin 
    inherited Create(AOwner); 
    FSQL := TStringList.Create; 
    FCreateViewSQL := DEFAULTCREATEVIEWSQL; 
    FDropViewSQL := DEFAULTDROPVIEWSQL; 
    FTableNameFormat := DEFAULTTABLENAMEFORMAT; end; 
    destructor TLiveQuery.Destroy; begin 
    if Active then begin Close; 
    DropTemporaryView; end; 
    SQL.Free; 
    inherited Destroy; end; 

    506	Часть IV. Темы повышенной сложности 

    procedure TLiveQuery.SetQuery(Value: TStrings); begin 
    ChecklnActive; 
    SQL.Assign(Value); end; 
    procedure TLiveQuery.CreateTemporaryView; var 
    TemporaryDB : TDatabase; WorkSQL : TStrings; begin 
    WorkSQL := TStringList.Create; WorkSQL.AddStrings(SQL); 
    TableName:=Format(TableNameFormat, [FormatDateTime('yymmddhhnnss',Now)]); 
WorkSQL.Insert(0,Format(CreateViewSQL, [TableName])); 
TemporaryDB:=Session.OpenDatabase(DatabaseName); if (TemporaryDBOnil) then try 
    if (TemporaryDB.IsSQLBased) then begin 
    if (DbiQExecDirect(TemporaryDB.Handle, qrylangSQL, 
    PChar(WorkSQL.Text),nil)<>DBIERR_NONE) then 
    raise EDatabaseError.Create('Error creating temporary view'); end else 
    raise EDatabaseError.Create('Cannot use this component with local tables'! finally 
    Session.CloseDatabase(TemporaryDB) ; WorkSQL.Free; end; end; 
    procedure TLiveQuery.DoBeforeOpen; begin 
    inherited DoBeforeOpen; 
    CreateTemporaryView; end; 
    procedure TLiveQuery.DropTemporaryView; var 
    TemporaryDB : TDatabase; WorkSQL : TStrings; begin 
    WorkSQL:=TStringList.Create; 
    WorkSQL.Add(Format(DropViewSQL, [TableName])); 
TemporaryDB:=Session.OpenDatabase(DatabaseName); if (TemporaryDBOnil) then 
begin try 
    if (TemporaryDB.IsSQLBased) then begin if 
(DbiQExecDirect(TemporaryDB.Handle,qrylangSQL, 
PChar(WorkSQL.Text),nil)<>DBIERR_NONE) then 
    raise EDatabaseError.Create('Error dropping temporary view'); end else 
    raise EDatabaseError.Create('Cannot use this component with local tables') finally 
    Session.CloseDatabase(TemporaryDB); WorkSQL.Free; end; end; end; 
    procedure TLiveQuery.DoAfterClose; begin 
    DropTemporaryView; 
    inherited DoAfterClose; end; 

    Глава 26. Borland Database Engine 507 

    procedure  Register;	I 
    begin	I 
    RegisterComponents('Data	Access',    [TLiveQuery]);                        
                                                                       I 
    end;	I 
    end.	I 

    Обратите внимание на использование функции DBiExecDirect. После создания в 
программе операто- 1 pa SQL WorkSQL необходимо выполнить его. Однако ввиду 
использования потомка компонента Table не- J возможно выполнить этот оператор 
немедленно. Если бы компонент LiveQuery был потомком компонен- 1 та Query, то 
можно было бы обратиться к процедуре ExecSQL, но в данном случае это не так. 
Вот здесь в 1 игру и вступает функция DBiExecDirect. Для выполнения запроса 
SQL создается временный объект | hDBIDb, который передается в функцию вместе с 
содержимым WorkSQL.	1 
    Если бы в данном случае не требовалась совместимость с SQL Server, то 
вместо открытого компонен-;/ том LiveQuery временного подключения к серверу 
можно было бы использовать свойство DBHandle этого компонента. Система SQL 
Server запрещает инициирование нового запроса, пока предыдущий находится в 
состоянии ожидания, поэтому в данной СУБД нерационально использовать свойство 
    LiveQuery. Database. Handle совместно с функцией DBiExecDirect. 
    Обратите внимание на то, что третий параметр функции DBiQExecDirect должен 
иметь тип PChar. На-	j помним, что строки типа PChar в языке Pascal являются 
аналогами строк типа char   * в языках С и C++.	j 
    Начиная с версии Delphi 2.0, преобразование строк в стиле Pascal к строкам 
в стиле C/C++ сводится к	: простому приведению типа. Строки в языке Pascal 
теперь заканчиваются нулевым символом, но, кроме	j того, содержат поле длины, 
поэтому для определения длины строки, в отличие от языка С, не нужно про-	j 
сматривать всю строку до конца.	| 

    Прямое обращение к СУБД 

    С помощью BDE можно обращаться непосредственно к средствам обслуживания 
подключений к серверу конкретной СУБД, т.е. вызывать функции ее 
пользовательских библиотек, минуя компоненты баз данных Delphi и драйверы BDE. 
В некоторых случаях может оказаться, что использование таких вы-   > зовов 
существенно повышает быстродействие или предоставляет функциональные 
возможности, отсут-ствующие в BDE. 
    Для обращения к обработчику подключений СУБД используется функция DbiGetProp. В табл. 26.3  

    Таблица 26.3. Информация о СУБД, получаемая с помощью функции DbiGetProp 

СУБД                                           Тип обработчика                                        Длина 

InterBase                                      gds_db_handle                                         4 
Sybase                                         DBPROCESS NEAR *                             2 
Oracle                                           LDA                                                           64 
ODBC Socket                              HDBC                                                         4 


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

    type 
    DBPROCESS = Pointer; 
    var 
    Forml:  TForml; DBProc: DBPROCESS; SizeDBProc: Word; 

    508	Часть IV. Темы повышенной сложности 

 
    function dbname(DBProc: DBPROCESS) : PChar; external 'LIBSYBDB'; 
    implementation ($R *.DFM} 
    procedure TForml.ButtonlClick(Sender: TObject); begin 
    DbiGetProp(hDBiObj(Tablel.DBHandle),dbNATIVEHNDL, DBProc, SizeOf(DBProc), SizeDBProc); 
    Editl.Text:=dbname(DBProc); end; 

    В этом примере вызывается функция dbname, которая находится во внешней 
динамической библиотеке LIBSYBDB. Функция принимает один параметр — обработчик 
DBPROCESS текущего соединения. Поиск этого обработчика выполняется вызовом 
процедуры DbiGetProp с параметром dbNATIVEHNDL. 

    Получение информации о СУБД через ВОЕ 

    Если требуется предоставить пользователю возможность формирования запросов 
в ходе работы приложения, то необходимо иметь доступ не только к строкам и 
столбцам таблиц выбранной СУБД, но и к списку функций, которые поддерживаются 
системой для обращения к данным. Необходимо уметь отображать на экране такую 
информацию, как поддерживаемые данной СУБД функции вычисляемых столбцов, 
обработки строк, дат и чисел и т.д. Для всего этого в BDE имеется функция под 
названием DbiOpenFunctionList. Эта функция открывает курсор, указывающий на 
список функций соответствующей СУБД. После этого для поочередного получения 
имен отдельных функций используется функция DbiGetNextRecord. Листинги 
26.3—26.5 содержат демонстрационную программу Delphi, в которой используется 
функция DbiOpenFunctionList для получения списка объектов таблиц в текущей 
СУБД, а также функций, поддерживаемых этой платформой. Программа представляет 
на экране раскрывающийся список псевдонимов BDE, к которым можно обращаться с 
запросами о таблицах и функциях. 

    Листинг 26.3. Файл проекта для программы FuncEx демонстрации функции BDE DbiOpenFunctionList 

    program FUNCEX; 
    uses Forms, FUNCEXOO in 'FUNCEXOO.PAS ' {Forml}; 
    ($R *.RES} 
    begin 
    Application.Initialize; 
    Application.CreateForm(TForml, Forml); 
    Application.Run; end. 
    Листинг 26.4. Исходный текст модуля FuncExOO. PAS демонстрационной 
программы FuncEx 
    unit FUNCEXOO; interface 
    uses 
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 
StdCtrls, DBTables, BDE; 
    type 
    TForml = class(TForm) Labell: TLabel; Label2: TLabel; 

    Глава 26. Borland Database Engine 509 

    Label3: TLabel; 
    ComboBoxl: TComboBox; 
    ListBoxl: TListBox; 
    ListBox2: TListBox; 
    Databasel: TDatabase; 
    procedure FormCreate(Sender: TObject); 
    procedure ComboBoxlChange(Sender: TObject); private 
    { Private declarations } public 
    { Public declarations } end; 
    var 
    Forml: TForml; 
    implementation {$R *.DFM) 
    procedure TForml.FormCreate(Sender: TObject); begin 
    Session.GetDatabaseNames(Comboboxl.Items); 
    Comboboxl.Itemlndex:=0; end; 
    procedure TForml.ComboBoxlChange(Sender: TObject); function Chk(ErrorValue 
: DBIResult) : DBIResult; var 
    dbi_status : String; 
    dbi_string : String; 
    Errlnfo  : DBIErrlnfo; 
    count : Integer; begin 
    dbi_status:=''; 
    dbi_string:=''; 
    if (ErrorValue <> DBIERR_NONE) then begin DbiGetErrorInfo(TRUE, Errlnfo); 
    if (Errlnfo.iError = ErrorValue) then begin dbi_status:='  ERROR '+Errlnfo.szErrCode; 
    With Errlnfo do 
    for count:=low(szContext) to high(szContext) do 
    if (strcomp(Errlnfo.szContext[count], '') <> 0) then 
    dbi_status :=  dbi_status+'    '+Errlnfo.szContext[count]; end else begin 
    SetLength(dbi_string, DBIMAXMSGLEN); DbiGetErrorString(ErrorValue, 
PChar(dbi_string)); dbi_status := '  ERROR '+dbi_string; end; MessageBox (0, PChar(dbi_status), 
    'BDE Error',~MB_OK or MB_ICONEXCLAMATION); end; 
    result:=ErrorValue; end; var 
    hCur : hDBiCur; Funclnfo : DBiFUNCDESC; Counter : integer; begin 
    With Databasel do begin Connected:=False; ListBox2.Items.Clear; DatabaseName:=ComboBoxl.Items[ComboBoxl.Itemlndex]; 

    510	Часть IV. Темы повышенной сложности 

    Open; 
    Session.GetTableNames(DatabaseName,'',False,False, ListBoxl.Items); end; 
    Counter:=0; 
    if (Chk(DbiOpenFunctionList(Databasel.Handle, fnListINCL_USER_DEF, @hCur)) 
= DBIERR_NONE) then begin if (hCurOnil) then begin 
    while (DBiGetNextRecord(hCur,dbinolock,SFundnfo, nil)<>DBIERR_EOF) and 
(Counter<50) do begin 
    ListBox2.Items.Add(Funclnfo.szName); Inc(Counter); end; 
    DbiCloseCursor(hCur); ListBox2.Sorted:=True; 
    end else ShowMessage('Error opening cursor'); end; end; 
    end. 

    Листинг 26.5. Файл формы (DFM) модуля FuncExOO. PAS демонстрационной 
программы FuncEx 

    object Forml: TForml Left = 200 Top = 108 Width = 434 Height = 321 Caption 
= 'Forml' Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height 
= -11 Font.Name = 'MS Sans Serif Font.Style = [] OnCreate = FormCreate 
PixelsPerlnch = 96 TextHeight = 13 object Labell: TLabel 
    Left = 33 
    Top = 0 
    Width = 36 
    Height = 13 
    Caption = 'Aliases:' end object Label2: TLabel 
    Left = 32 
    Top =40 
    Width = 35 
    Height = 13 
    Caption = 'Tables:' end object Label3: TLabel 
    Left = 216 
    Top =40 
    Width = 98 
    Height = 13 
    Caption = 'Supported functions:' end object ComboBoxl: TComboBox 
    Left = 32 
    Top = 14 
    Width = 177 
    Height = 21 
    Style = csDropDownList 
    ItemHeight = 13 

    Глава 26. Borland Database Engine	511 

    Aliases- _____________ ТаЫек                                         

Suppcrted functions: 
    CALL	UPPER 
    EMPLOYEE 
    LEASE 
    PROPERTY 
    TENANT 
    WODETAIL 
    WORDER 
    WORKTYPE 

    Puc. 26.1. Список доступных таблиц и функций СУБД в окне приложения FuncEx 

    TabOrder = 1 
    OnChange = ComboBoxlChange end object ListBoxl: TListBox 
    Left = 32 
    Top = 56 
    Width = 177 
    Height = 209 
    ItemHeight = 13 
    TabOrder = 0 end object ListBox2: TListBox 
    Left = 216 
    Top =56 
    Width = 177 
    Height = 209 
    ItemHeight = 13 
    TabOrder = 2 end object Databasel: TDatabase 
    DatabaseName = 'DBDEMOS' 
    SessionName = 'Default' 
    Left = 384 
    Top = 8 end end 

    На рис. 26.1 показана программа FuncEx в действии. 

    Вычисляемые индексы 

    Поскольку BDE поддерживает вычисляемые индексы dBASE, то имеется 
возможность использования этой весьма гибкой системы вычисления выражений в 
пользовательских приложениях Delphi. Система вычисления выражений поддерживает 
все операторы и функции, доступные для индексов dBASE. Ее можно использовать 
как обычный калькулятор или для решения более сложных задач по вычислению выражений. 
    Система вычисления выражений BDE используется в приложениях путем создания 
индексного выражения над пустой таблицей dBASE формата DBF. Для этого создайте 
таблицу dBASE, содержащую одну строку (ее структура не имеет никакого 
значения) и настройте приложение на ввод выражения. Это выражение совсем не 
обязательно должно иметь что-либо общее с таблицей — таблица создается только 
для того, чтобы на ее основе создать индекс для вычисления выражений. После 
ввода выражения создайте индекс для таблицы, используя введенное выражение в 
качестве ключа. Затем сделайте новый индекс текущим индексом таблицы и 
воспользуйтесь функцией BDE DbiExtractKey для получения результата вычисления 
ключа. В итоге после всех проделанных операций вы получите значение введенного 
выражения. Вот фрагмент демонстрационной программы, в которой используется 
этот прием: 

    procedure  TForml.ButtonlClick(Sender:   TObject); var 
    IndexDesc   :   IDXDesc; KeyString   :   String; begin 
    With Tablel  do begin 
    if not Active  then Open; 
    Addlndex('EXPEVAL',Editl.Text,[ixExpression]); 
    Close; 
    IndexName:='EXPEVAL'; 
    Open; 
    DbiGetlndexDesc(Tablel.Handle,0,IndexDesc); 
    SetLength(KeyString,IndexDesc.iKeyLen); 
    DbiExtractKey(Tablel.Handle,nil,PChar(KeyString)); 
    Edit2.Text:=KeyString; 
    Close; 

    512 Часть IV. Темы повышенной сложности 

    Deletelndex('EXPEVAL'); IndexName:='DUMMY'; end; end; 

    Ключевую роль в этом фрагменте играет процедура DbiExtractKey. Она 
возвращает транслированное или вычисленное значение ключа индекса. Эта 
хитроумная процедура позволяет снабдить приложение Delphi чем-то наподобие 
интерпретатора иди синтаксического анализатора выражений. На рис. 26.2 
показано окно приложения, которое использует процедуру DbiExtractKey для 
вычисления сложных выражений. 

    Enlej your enptession hefe~ 
    THE '-.LTR!M(STR(3)K MUSKETEERS' 
    Evaluation: 
    THE 3MUSKETEERS1 

    Puc. 26.2.     Приложение,    использующее    функцию DbiExtractKey для 
вычисления выражений 

    Оптимизация ВОЕ 

    Оптимизация BDE — сложный и неоднозначный процесс, но и он подчиняется 
некоторым общим принципам. Конечно, панацеи здесь, как и во многих других 
областях, не существует; в конечном счете может оказаться, что система 
работает достаточно быстро и оптимизировать ее просто незачем. 
    U  Существенно замедляет модификацию данных большое количество вторичных 
индексов. Ограничьтесь только теми, которые действительно необходимы. Может 
оказаться, что намного быстрее убрать все индексы, модифицировать данные и 
перестроить индексы заново. 
    О  Открытие таблицы в монопольном режиме ускоряет доступ к ней, поскольку 
другие пользователи просто не принимаются в расчет. 
    О   Всегда, когда это возможно, следует избегать построчной работы с 
данными. Используйте вместо нее пакетные операции BDE. 
    Q   Работая с функцией DbiWriteBlock, старайтесь применять блоки, кратные 
размерам физических блоков. 
    Q Для принудительного создания файла LCK можно использовать функцию 
DbiAcqPersistTableLock над пустой таблицей, чтобы избежать повторяющегося 
удаления и создания этого файла при открытии и закрытии таблиц. 
    Q   Размещайте рабочие таблицы в оперативной памяти, а не на диске. При 
этом следует помнить, что над такими таблицами нельзя в произвольный момент 
создать индекс, выполнить пакетное копирование или сохранение. 

    Оптимизация доступа к ВОЕ с использованием SQL 

    Оптимизация операторов SQL более подробно рассматривается в главе 24, 
"Дополнительные сведения об SQL", и в главе 25, "Оптимизация приложений 
архитектуры клиент/сервер". Здесь приводится лишь несколько общих советов по 
оптимизации операторов SQL для доступа к BDE. 
    Q   При выполнении серии модификаций на сервере SQL инициируйте транзакции 
явным образом, чтобы избежать их неявного инициирования и сохранения после 
каждой отдельной модификации . 

    Глава 26. Borland Database Engine 513 17   Delphi 3 и системы клиент/сервер 

    Q  Настройте сложные запросы и хранимые процедуры на использование режима 
Passthrough. Это заставит систему BDE посылать запросы на сервер с 
максимальной скоростью и даст возможность использовать любой внутренне 
поддерживаемый код SQL. 
    Q  Как уже упоминалось, используя информацию, полученную с помощью функции 
DbiGetProps интерфейса IDAPI, можно выполнять прямые вызовы функций СУБД. 
    Q  Основной задачей оптимизации должна стать минимизация размера набора 
данных, возвращаемого запросом. Группирование и объединение следует выполнять 
уже после минимизации. Большая часть этой работы должна выполняться на сервере. 
    Q  В некоторых случаях можно добиться повышения производительности, 
копируя результаты запроса в локальную таблицу. 
    Q   Использование функций DbiAddFilter, DbiSetRange и DbiSetFieldMap 
помогает значительно уменьшить объем реально обрабатываемых данных. Всякий 
раз, когда объем перебираемых данных уменьшается, быстродействие закономерно 
возрастает. Следует заметить, что все три функции поддерживаются компонентами 
семейства DataSet. 
    О  Создание убывающего индекса в таблице, которую часто приходится 
прокручивать в обратном порядке, позволяет существенно сэкономить время работы сервера. 
    Q  Находясь в середине большой таблицы или сложного индекса, следует 
избегать применения функций DbiSetToEnd и DbiSetToKey. Если вызвать 
какую-нибудь из этих функций в неподходящий момент, то приложению придется 
последовательно перебрать большое количество строк. 

    Резюме 

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

    Что дальше... 

    Глава 27 содержит основные принципы построения компонентов баз данных для 
использования в приложениях Delphi. Вы научитесь создавать компоненты самого 
разного уровня сложности и рационально использовать их в своих приложениях. 

    514	Часть IV. Темы повышенной сложности 
