background image

 

Rozdział 26 

Borland Database Engine (BDE) 

Borland Database Engine (BDE) (mechanizm dostępu do baz danych) jest 
zestawem bibliotek DLL, przy pomocy których Delphi realizuje dostęp do baz 
danych. Interfejs BDE jest używany nie tylko przez Delphi. Posługują się nim 
również inne programy firmy Borland (takie jak Paradox for Windows, Visual 
dBASE, Borland C++ Builder itd). W niektórych pakietach programowych tej 
firmy, BDE występuje pod starą nazwą IDAPI (Independent Database Application 
Programming Interface). Początkowo IDAPI miał być interfejsem otwartych baz 
danych, ale na tym polu przegrał z ODBC firmy Microsoft.  

Delphi, poprzez swoje komponenty przeznaczone do obsługi baz danych, zawiera 
prawie wszystkie właściwości funkcjonalne BDE, toteż niezwykle rzadko zachodzi 
konieczność bezpośredniej współpracy z BDE. Metody oferowane przez Delphi są 
bez porównania łatwiejsze i bardziej odporne na błędy, niż bezpośrednie 
wywołania BDE. Zastosowanie tych komponentów nie redukuje (wbrew pozorom) 
efektywności aplikacji.  

Stosując bezpośrednie odwołania BDE należy pamiętać o kilku sprawach. Po 
pierwsze - nie jest dostępny mechanizm obsługi wyjątków, który biblioteka Delphi 
- VCL - zapewnia podczas dostępu do bazy danych. Należy dokładnie sprawdzać 
kody zwracane przez funkcje BDE i odpowiednio na nie reagować. Po drugie - 
z uwagi na fakt, iż komponenty bazy danych Delphi nie będą świadome niskiego 
poziomu dostępu, musimy się liczyć z koniecznością ponownej synchronizacji 
z bazą danych. W pewnych sytuacjach może to wymagać tylko wywołania metody 
Refresh (odświeżania), w innych - zamknięcia i ponownego otwarcia objektu 
DataSet. 

W tym rozdziale przyjrzymy się wykorzystaniu BDE, w celu rozszerzenia dostępu 
do baz danych w stosunku do zakresu oferowanego przez bibliotekę Delphi - VCL. 
Nie traktujemy tutaj BDE jako alternatywy dla VCL, a jedynie jako jej 
rozszerzenie. Omówimy podstawową strukturę BDE i funkcje BDE (w zakresie 
wymaganym przez twórców aplikacji) oraz metody jednoczesnego stosowania 
komponentów Delphi i odwołań BDE API.  

BDE kontra ODBC 

W zasadzie sterowniki BDE, na tle swych odpowiedników ODBC, wypadają 
korzystniej - są szybsze i 

charakteryzują się lepszymi właściwościami 

background image

746 

Część IV 

użytkowymi. Do wielu sterowników nie można ponadto uzyskać dostępu bez 
wykonywania bezpośrednich odwołań ODBC API - nie są one obsługiwane przez 
większość narzędzi poza tymi, które pretendują do zgodności z ODBC.  

Inaczej jest w przypadku BDE. Z zasady, oparte o BDE sterowniki SQL Link 
obsługują platformy bazy danych w sposób kompleksowy. Ich funkcjonalność 
potwierdza się w pracy komponentów baz danych Delphi. Np. serwery baz danych 
SQL dysponują mechanizmem kontroli transakcji, który umożliwia grupowanie 
porcji zmian w bazie danych z zachowaniem (lub bez) ich kompletności. Składnia 
SQL wymaga zapoczątkowania zmian grupy transakcji z serwera do serwera.  

Z drugiej strony tabele lokalne nie pozwalają na stosowanie przetwarzania 
transakcyjnego. Mimo sprzeczności między producentami w zakresie składni SQL 
i mimo braku obsługi transakcji w tabelach lokalnych, BDE zapewnia pojedyncze 
odwołanie API do rozpoczęcia transakcji na dowolnej platformie: 

DbiBeginTran

. Ta właściwość znajduje nawet zastosowanie dla tabel 

lokalnych; BDE tworzy dla nich swój własny mechanizm kontroli transakcji. 
Delphi zawiera to odwołanie BDE API w metodzie 

BeginTransaction

 

swojego komponentu 

TDatabase

, co pozwala na zainicjowanie transakcji na 

platformie serwera, bez odwoływania się do tabeli aktualnej. Tym samym można 
stosować przetwarzanie transakcyjne tabel lokalnych, bez względu na 
ograniczenia. BDE zapewnia jednolity interfejs API, bez względu na rodzaj 
stosowanego systemu zarządzania bazą danych (DBMS).  

Architektura  

Architektura BDE bazuje na sterownikach - dla każdego stosowanego systemu 
obsługi baz danych (DBMS) wymagany jest oddzielny sterownik. Czasami 
pojedynczy sterownik obsługuje wiele wersji platform DBMS (np. istnieje jeden 
sterownik dla platformy dBASE, bez względu na to, czy mamy do czynienia 
z dBASE III, IV lub V, FoxPro lub systemem Clipper).  

Rozważając to zagadnienie, lepiej zrozumiemy architekturę BDE. Dostęp z Delphi 
do obiektu bazy danych realizowany jest następująco:  

1. Odwołanie się do komponentu bazy danych Delphi.  

2. Komponent wywołuje BDE.  

3. BDE wywołuje swoją lokalną bazę danych lub sterownik SQL Links.  

4. Jeśli używany jest sterownik lokalny, wówczas udostępnia on tabele bazy 

danych  

5. Jeśli używany jest sterownik SQL Links, wywołuje on sterownik klienta 

producenta DBMS.  

background image

 

Rozdzial 26 Borland Database Engine (BDE) 

747

 

BDE jest zorientowany obiektowo - charakteryzuje się wygodną obsługą 
i łatwością rozszerzania. Jeśli zachodzi potrzeba uzyskania dostępu do platformy 
DBMS, dla której BDE nie zapewnia pełnej obsługi, należy po prostu zainstalować 
- dedykowany dla tej platformy - sterownik BDE lub ODBC. Jest mało 
prawdopodobne, aby trzeba było przerabiać  BDE,  by  mógł obsługiwać nowe 
platformy.  

BDE jest przygotowany do pracy w trybie klient/serwer - obsługuje wszystkie 
zaawansowane funkcje, których użytkownicy oczekują od DBMS typu 
klient/serwer. Mamy tutaj na myśli m.in.: przetwarzanie transakcji, aktualizowanie 
kursorów, obsługę procedur pamiętanych i inne. Zamiast stosować metodę 
najmniejszego wspólnego mianownika do obsługi baz danych, BDE dysponuje 
interfejsem API, który jest „superzestawem” funkcji dostępnych na wszystkich, 
obsługiwanych przez niego platformach. Ponieważ sterowniki i API nie są z sobą 
w powiązane w BDE, przejście z platformy na platformę nigdy nie było łatwiejsze. 
Skalowanie związane z 

przejściem z 

tabel lokalnych na platformy typu 

klient/server jest tak łatwe, jak zmiana aliasu bazy danych, stosowanego przez 
aplikację do wskazania nowej platformy.  

Usługi współdzielone  

Mocna strona BDE wynika z faktu, iż oferuje on usługi, które mogą być 
współdzielone przez sterowniki baz danych. Często dzięki temu nie trzeba 
ustanawiać oddzielnych usług dla każdego sterownika. Ma to pozytywny wpływ na 
ogólną efektywność aplikacji i zapewnia zgodność pomiędzy driverami.  

Usługi systemu operacyjnego (OS)  

Ponieważ BDE udostępnia swoim sterownikom pełny zestaw usług, izolując je od 
bezpośredniego kontaktu z systemem operacyjnym i siecią, jest on w dużej mierze 
od niego niezależny.  

Menadżer bufora  

Pierwszą usługą systemu operacyjnego jest Współdzielony menadżer bufora
oparty na priorytetach i umożliwiający współdzielenie tego samego dynamicznego 
obszaru bufora przez wszystkie sterowniki BDE. Aczkolwiek stosowanie obszaru 
dynamicznego nie jest konieczne, sterowniki wykorzystujące wspólny obszar 
dynamiczny oszczędzają zasoby systemu.  

background image

748 

Część IV 

Menadżer pamięci  

Aby zwiększyć wydajność przydziału mniejszych obszarów pamięci, BDE 
dysponuje swoim własnym mechanizmem przydziału pomocniczego. Dzięki niemu 
sterowniki nie muszą każdorazowo odwoływać się do systemu operacyjnego, gdy 
potrzebują małej ilości pamięci. BDE przydziela większe fragmenty pamięci RAM 
i następnie przekazuje je klientowi zgłaszającemu żądania jej przydziału.  

Bufor danych typu BLOB 

W celu przyspieszenia dostępu do obiektów typu BLOB, BDE zapewnia bufor 
danych BLOB. Pamięć ta uwalnia programistów od konieczności zakładania 
własnych buforów. Umożliwia ponadto swobodny dostęp do informacji typu 
BLOB  -  tak,  by  użytkownicy BDE nie musieli się zajmować zapisywaniem 
zawartości tego typu danych do pliku zewnętrznego. Ten swobodny dostęp 
realizowany jest nawet na platformach, które - ze swojej strony - nie zapewniają 
dostępu do danych tego typu. 

Mechanizm sortowania  

BDE zapewnia szybki mechanizm sortowania, wykorzystywany przez mechanizm 
zapytań, sterowniki dBASE i Paradoxa. Firma Borland posiada dwa różne patenty 
na architekturę tych mechanizmów sortowania.  

Mechanizm zapytań  

Aparat zapytań BDE jest mocnym, pełnowartościowym procesorem zapytań, który 
szybko wytwarza pełne zestawy wynikowe. Mechanizm ten obsługuje zarówno 
metody  Zapytanie przez przykład (QBE- Query By Example) i bezpośrednio 
metody SQLa - zapytywania baz danych za pośrednictwem każdego, 
obsługiwanego przezeń sterowników. SQL można np. wykorzystywać do zapytania 
tabel dBASE, nawet w przypadku tabel lokalnych, pozbawionych właściwej 
obsługi przetwarzania zapytań SQL.  

Generator SQL 

Jak już wspomniano, BDE umożliwia tworzenie zapytań baz danych przy pomocy, 
znanej z Paradoxa, metody QBE lub używając tradycyjnych instrukcji SQL. Gdy 
zapytanie QBE przesyłane jest do serwera SQL, tłumaczone jest najpierw przez 
BDE na SQL.  

background image

 

Rozdzial 26 Borland Database Engine (BDE) 

749

 

Restrukturyzacja  

Usługa współdzielona Restrukturyzacja umożliwia wykonanie modyfikacji 
struktur tabel dBASE i Paradox, bez konieczności zaznajamiania się z szczegółami 
tego procesu. Obsługuje ona automatyczne tworzenie tymczasowych tabel lub 
zapisywanie i ponowne ładowanie danych.  

Usługa translacji danych  

BDE dostarcza usługę realizującą translację danych pomiędzy różnymi formatami. 
Właściwość ta pozwala funkcjom i usługom na operacje między bazami danych. 
W przypadku konwersji pomiędzy podobnymi formatami, usługa określa najlepszą 
metodę konwersji i wykonuje ją.  

Funkcje integralnej obsługi tabel  

BDE zapewnia potężny mechanizm służący do przenoszenia danych z jednego 
formatu na inny oraz do obsługi łączenia podobnych do siebie tabel. Usługa ta jest 
podstawą komponentu Delphi - 

TBatchMove

 i programu narzędziowego Data 

Pump.  

Tabele w pamięci  

Podobnie w przypadku, gdy mamy do czynienia z wieloma platformami DBMS 
klient/serwer, BDE obsługuje tabele tymczasowe, które istnieją tylko w pamięci. 
Usługa ta przyspiesza dostęp do danych, zwiększa efektywność sortowania 
i zapewnia niezbędne podstawy dla uaktualnień buforowanych.  

Kursory łączone 

BDE obsługuje kursory łączone na poziomie mechanizmu, zaspakajając potrzebę 
VCL Delphi, uwalnia jednocześnie projektanta aplikacji od zajmowania się tym 
problemem.  Łączenie jednego kursora z 

innym powoduje przejście 

przemieszczanie się jednego kursora w czasie zmian drugiego. Zwykle kursor 
łączony stosowany jest podczas ustanawiania związków ogólny/szczegółowy 
między tabelami.  

Usługi sterowników SQL 

Dla sterowników bazujących na SQL dostarczana jest seria mini usług - m.in. 
translacja komend nawigacyjnych do SQL-a (dzięki czemu dysponować możemy 

background image

750 

Część IV 

łatwiejszym dostępem do tabel lokalnych i SQL), słownik danych i usługi 
katalogowe obiektów oraz manipulacja buforowanymi polami typu BLOB.  

Menadżer systemu  

W razie konieczności Menadżer systemu dla BDE ładuje sterowniki i zwalnia 
zasoby, gdy nie są one już dłużej potrzebne.  

Menadżer konfiguracji  

Usługa zarządzania konfiguracją BDE umożliwia klientom odczyt i 

zapis 

informacji dotyczącej konfiguracji sterownika. Menadżer umożliwia tworzenie 
aliasów, zapytań o parametry sterowników itd.  

Sterowniki językowe  

Dzięki sterownikom językowym BDE, twórcy aplikacji nie muszą się martwić 
o zachowanie BDE w przypadku zmiany języka. 

BDE API 

BDE API jest interfejsem użytkownika API, zapewniającym pojedynczy 
i zunifikowany zestaw odwołań API, zarówno dla zasobów baz danych opartych 
o ISAM, jak i zdalnych. Każdy typ bazy danych ma dedykowany sobie interfejs 
API. Stąd nawet, gdyby dwukierunkowa nawigacja pomiędzy obiektami bazy 
danych serwera SQL nie była właściwie obsługiwana przez producentów DBMS, 
Delphi zapewnia ją (w obu kierunkach) w zdalnych zbiorach wynikowych (czego 
moglibyśmy oczekiwać normalnie od tabel lokalnych, ale nie od zdalnych). Co 
więcej - mimo, iż dBASE i Paradox, jako formaty plików lokalnych, nie posiadają 
wbudowanej obsługi transakcji, BDE zapewnia dla nich mechanizm kontroli 
transakcji, który wykorzystuje te same odwołania API jak platformy, które te 
transakcje obsługują.  

BDE API (co można odczytać jedynie pozytywnie) nie obsługuje wszelkich 
elementów właściwych każdej platformie DBMS, dla której pracuje. Stąd np. 
indeksy systemu Clipper są teraz obsługiwane przez sterownik BDE dBASE, 
indeksy pierwotne natomiast - przez sterownik systemu Paradox. Procedury 
pamiętane obsługiwane są na tych platformach SQL, które mają je wbudowane itd.  

background image

 

Rozdzial 26 Borland Database Engine (BDE) 

751

 

Minimalne właściwości funkcjonalne  

Na platformach, dla których dostępny jest rodzimy sterownik BDE lub ODBC, 
dostępne będą zawsze następujące funkcje:  

„Otwieranie/zamykanie baz danych  

„Pobieranie/nadawanie wartości właściwości w obiektach baz danych  

„Odczytywanie/zapisywanie danych w obiektach baz danych  

„Tworzenie obiektów baz danych, takich jak tabele i indeksy  

„Wykonywanie operacji między bazami danych - jak kopiowanie jednej bazy do 

innej  

Podstawowe struktury BDE  

Aby stworzyć aplikację współpracującą z BDE, powinniśmy znać podstawowe, 
wykorzystywane przez ten mechanizm, struktury danych (tabela 26.1):  

Tabela 26.1 Podstawowe struktury danych BDE  

Struktura  

Opis  

BATTb1Desc 

Integralny deskryptor tabeli  

CANHdr 

Nagłówek klasy węzła filtra 

CBPROGRESSDesc 

Procedura zwrotna wskaźnika postępu 

RESTcbDESC 

Procedura zwrotna restrukturyzacji tabeli 

CFGDesc 

Deskryptor konfiguracji 

CLIENTInfo 

Informacja o aplikacji klienta 

CRTb1Desc 

Deskryptor atrybutów tabeli 

DBDesc 

Deskryptor bazy danych 

DBIEnumFld 

Pole wyliczeniowe 

DBIEnv 

Deskryptor środowiska BDE 

DBIErrInfo 

Deskryptor informacji o błędzie 

DBIQryProgress 

Wskaźnik statusu zapytania 

DRVType 

Deskryptor informacji o sterowniku 

FILEDesc 

Deskryptor pliku 

background image

752 

Część IV 

Struktura  

Opis  

FILTERInfo 

Deskryptor informacji o filtrze 

FLDDesc

 

Deskryptor pola  

FLDType

 

Deskryptor typu pola 

FMLDesc 

Deskryptor sterownika języka 

FMTBcd 

Format liczby dziesiętnej kodowanej dwójkowo 

FMTDate 

Format danej 

FMTNumber 

Format liczby 

FMTTime 

Format czasu 

DbiFUNCArgDesc 

Deskryptor argumentu funkcji zdalnego zasobu 
danych 

DbiFUNCDesc 

Deskryptor funkcji zdalnego zasobu danych 

IDXDesc 

Deskryptor indeksu 

IDXType 

Deskryptor typu indeksu 

LDDesc 

Deskryptor sterownika języka 

LOCKDesc 

Deskryptor blokady 

RECProps 

Deskryptor własności rekordu 

RINTDesc 

Deskryptor integralności 

SECDesc 

Deskryptor ochrony 

SESInfo 

Deskryptor informacji o sesji 

SPDesc 

Deskryptor procedury standardowej 

SPParamDesc 

Deskryptor parametrów procedury standardowej 

SYSConfig 

Deskryptor informacji o konfiguracji systemu 

SYSInfo 

Deskryptor statusu systemu BDE 

SYSVersion 

Deskryptor informacji o wersji systemu BDE 

TBLBaseDesc 

Deskryptor informacji o tabeli podstawowej 

TblExtDesc 

Deskryptor informacji o tabeli dodatkowej 

TBLFullDesc 

Deskryptor informacji o tabeli pełnej 

TBLType 

Deskryptor możliwości tabeli 

background image

 

Rozdzial 26 Borland Database Engine (BDE) 

753

 

Struktura  

Opis  

USERDesc 

Deskryptor użytkownika 

VCHKDesc 

Deskryptor sprawdzenia ważności 

XInfo

  

Deskryptor transakcji 

Oprócz tych struktur BDE definiuje obiekty podane w tabeli 26.2.  

Tabela 26.2 Obiekty definiowane przez BDE  

Obiekt 

Typ  

Opis  

hDBICur hDBIObj 

Uchwyt kursora 

hDBIDb hDBIObj 

Uchwyt bazy danych 

hDBIObj UINT32 

Uchwyt obiektu nieokreślonego 

hDBIQry hDBIObj 

Uchwyt zapytania 

hDBISes hDBIObj 

Uchwyt sesji 

hDBIStmt hDBIObj 

Uchwyt instrukcji zapytania 

hDBIXact UINT32 

Uchwyt transakcji 

hDBIXlt hDBIObj 

Uchwyt translacji 

phDBICfg 

^

hDBICfg 

Wskaźnik uchwytu konfiguracji 

phDBICur 

^

hDBICur 

Wskaźnik uchwytu kursora 

phDBIDb 

^

hDBIDb 

Wskaźnik uchwytu bazy danych 

phDBIObj 

^

hDBIObj 

Wskaźnik uchwytu nieokreślonego obiektu 

phDBIQry 

^

hDBIQry 

Wskaźnik uchwytu zapytania 

phDBISes 

^

hDBISes 

Wskaźnik uchwytu sesji 

phdbistmt 

^

hdbistmt 

Wskaźnik uchwytu instrukcji 

phDBIXact 

^

hDBIXact 

Wskaźnik uchwytu transakcji 

phDBIXlt 

^

hDBIXlt 

Wskaźnik uchwytu translacji  

Najczęściej w aplikacjach używane są zmienne: 

hDBIDb

 i 

hDBICur

. Zmienna 

hDBIDb

 zwracana jest przy otwieraniu bazy danych, 

hDBICur

 - przy otwieraniu 

tabeli. Odpowiada to właściwościom 

DBHandle

 i 

Handle 

tabel TDBDataSet 

i TBDEDataSet.  

background image

754 

Część IV 

Budowanie aplikacji BDE  

Najprawdopodobniej nie będziemy autorami aplikacji, stworzonej wyłącznie przy 
pomocy odwołań BDE (tj. z pominięciem komponentów bazodanowych Delphi). 
Niemniej jednak wiedza o wymaganiach takiej aplikacji może być pożyteczna, 
ponieważ pomaga zrozumieć procesy, w wyniku których Delphi zapewnia dostęp 
do bazy danych. Aby stworzyć aplikacje stosując tylko odwołania BDE API, 
powinniśmy wykonać 12 podstawowych kroków:  

1. Zainicjować mechanizm BDE.  

2. Ustawić warstwę analizy programu (debug layer). 

3. Otworzyć bazę danych. 

4. Ustawić katalog roboczy.  

5. Ustawić katalog tymczasowy.  

6. Otwierając tabelę stworzyć kursor.  

7. Pobrać właściwości tabeli  

8. Ulokować obszar bufora rekordu.  

9. Przesunąć kursor na pożądany rekord.  

10. Pobrać rekord z kursora.  

11. Pobrać żądane pole z rekordu.  

12. Zwolnić wszystkie przydziały zasobów.  

Listing 26.1 ilustruje powyższe  12 kroków. W tym przykładzie dokonywana jest 
konwersja aplikacji wzorcowej (pobranej z Helpa BDE) - z języka C na Object 
Pascal. Po zakończeniu konwersji, aplikacja jest wstawiana do zdarzenia 

OnClick

 komponentu przycisku. 

Listing 26.1. Aplikacja wzorcowa BDE Help przepisana w Object 
Pascalu i 

poddana konwersji na obsługę zdarzenia OnClick 

przycisku.  

{*******************************************************} 

{                BDE Template Program 

{         Copyright (c) 1996 Borland International 

{  

{*******************************************************} 

Conversion to Object Pascal by Ken Henderson. 

background image

 

Rozdzial 26 Borland Database Engine (BDE) 

755

 


unit bdetemp; 

interface 

uses 
 

Windows, Messages, SysUtils, Classes, Graphics,  

 

 Controls, Forms, Dialogs, 

 

StdCtrls, ToolWin, ComCtrls, BDE; 

type 
 

TForm1 = class(TForm) 

  ToolBar1: 

TToolBar; 

  Button1: 

TButton; 

  procedure 

Button1Click(Sender: 

TObject); 

 private 
 

 

{ Private declarations } 

 public 
 

 

{ Public declarations } 

 end; 

var 
 Form1: 

TForm1; 

implementation 

{$R *.DFM} 

procedure TForm1.Button1Click(Sender: TObject); 
var 
 

hDb : hDBIDb;        // Handle to the Database 

 

hCur : hDBICur;      // Handle to the cursor (table) 

 

szTblName : String; 

 

szTblType : String; 

 

CursorProps : CURProps;  // Properties of the cursor 

 

pRecBuf : pBYTE;        // Pointer to the record buffer 

 

EmpNo : integer; 

 

isBlank : BOOL; 

function Chk(ErrorValue : DBIResult) : DBIResult; 
var 
 

dbi_status : string; 

 

dbi_string : string; 

 

ErrInfo : DBIErrInfo; 

 

count : integer; 

begin 
 dbi_status:=’’; 
 dbi_string:=’’; 
 

if (ErrorValue <> DBIERR_NONE) then begin 

  DbiGetErrorInfo(TRUE, 

ErrInfo); 

background image

756 

Część IV 

 

 

if (ErrInfo.iError = ErrorValue) then begin 

 

 

dbi_status:=’ ERROR ‘+ ErrInfo.szErrCode; 

  With 

ErrInfo 

do 

 

 

 

for count:=low(szContext) to high(szContext) do 

    if 

(strcomp(ErrInfo.szContext[count], 

 

 

 

 

 

 ‘’)<>0) then 

     dbi_status 

:= 

dbi_status+’ 

‘+ 

ErrInfo. 

 

 

 

 

 

 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 

engine’); 

 

Chk(DbiInit(nil)); // Step 2 

 

ShowMessage(‘Set debug layer options’); 

 

DbiDebugLayerOptions(DEBUGON or OUTPUTTOFILE or  

 

 FLUSHEVERYOP, ‘TRACE.TXT’);// Step 3 

 ShowMessage(‘Open 

database’); 

 Chk(DbiOpenDatabase( 

 // 

Step 

 

nil,  

 

// Database name - nil for standard  

 

 database 

 nil, 

 

 

// Database type - nil for standard  

 

 database 

 

dbiREADWRITE, 

// Open mode - Read/Write or Read only 

 

dbiOPENSHARED, 

// Share mode - Shared or Exclusive 

 nil, 

 

 

 

// Password - not needed for the  

 

 STANDARD database 

 0, 

   // 

Number 

of 

optional 

parameters 

 nil, 

 

 

 

// Field Desc for optional parameters 

 nil, 

 

 

 

// Values for the optional parameters 

 

hDb)); 

 

 

// Handle to the database 

 

ShowMessage(‘Set table directory’); 

background image

 

Rozdzial 26 Borland Database Engine (BDE) 

757

 

 Chk(DbiSetDirectory( 

 // 

Step 

 hDb, 

 

 

 

 

 

// Handle to the database which  

 

 is being modified 

 

‘C:\Program Files\Borland\Delphi 3.0\Demos\Data’)); 

 

 // The new working directory 

 

ShowMessage(‘Set private directory’); 

 Chk(DbiSetPrivateDir( 

 

// Step 6 

 

‘c:\temp’));  

 

 

// Select a directory on  

 

 a local drive not used 

       // 

by 

other 

applications. 

 szTblName:=’EMPLOYEE’; 
 szTblType:=szPARADOX; 
 ShowMessage(‘Open 

table’); 

 Chk(DbiOpenTable( 

  // 

Step 

 hDb, 

     // 

Handle 

to 

the 

standard 

 

 

 database 

 PChar(szTblName), 

 

 

// Name of the table 

 PChar(szTblType), 

 

 

// Type of the table - only  

 

 used for local tables 

 nil, 

     // 

Index 

Name 

Optional 

 nil, 

     // 

IndexTagName 

Optional. 

 

 

 Only used by dBASE 

 0, 

     // 

IndexId 

Primary. 

 

dbiREADWRITE, 

 

 

// Open Mode - Read/Write or  

 

 Read Only 

 

dbiOPENSHARED, 

 

 

// Shared mode - SHARED or EXCL 

 

xltFIELD, 

 

 

 

// Translate mode - Almost  

 

 always xltFIELD 

 FALSE, 

    // 

Unidirectional 

cursor 

 

 

 movement. 

 nil, 

     // 

Optional 

Parameters. 

 hCur)); 

    // 

Handle 

to 

the 

cursor 

 

ShowMessage(‘Get cursor properties’); 

 

Chk(DbiGetCursorProps( 

// Step 8 

 hCur, 

    // 

Handle 

to 

the 

cursor 

 

CursorProps)); 

 

 

// Properties of the cursor  

 

 (table) 

 

ShowMessage(‘Allocate a record buffer’); 

 

GetMem(pRecBuf,CursorProps.iRecBufSize * sizeof(BYTE));  

 

 // Step 9 

 

if (pRecBuf = nil) then 

  ShowMessage(‘Error 

allocating 

buffer’) 

 else 

begin 

background image

758 

Część IV 

 

 

ShowMessage(‘Set cursor to the crack before the  

 

 

 first record’); 

 

 

Chk(DbiSetToBegin(hCur)); // Step 10 

 

 

 // Position the specified cursor to the crack 

 

 

 // before the first record 

 

 

ShowMessage(‘Get the next record’); 

  Chk(DbiGetNextRecord( 

 

// Step 11 

  hCur, 

 

 

 

 // Cursor from which to get the record. 

  dbiNOLOCK, 

   // 

Lock 

Type 

  pRecBuf, 

 

 

 

 // Buffer to store the record 

  nil)); 

 

 

 

 // Record properties - don’t need in this case 

 

 

ShowMessage(‘Get a field out of the record buffer’); 

  Chk(DbiGetField( 
 

 

hCur, 

 

 

// Cursor which contains the record 

 

 

1,   

 

 

// Field Number of the “EmpNo”  

 

 

 field. 

  pRecBuf, 

 

 

// Buffer containing the record 

  @EmpNo, 

  // 

Variable 

for 

the 

EmpNo 

 

 

isBlank)); 

 

// Is the field blank? 

 

 

ShowMessage(‘The retrieved field  

 

 

value is ‘+ IntToStr(EmpNo)); 

 end; 

 ShowMessage(‘Clean-up’); 

 

if (pRecBuf <> nil) then 

 

freemem(pRecBuf); // Free the record buffer 

 

if (hCur <> nil) then 

 

 

Chk(DbiCloseCursor(hCur)); 

// Close the cursor 

 

if (hDb <> nil) then 

 

 

Chk(DbiCloseDatabase(hDb)); 

// Close the database 

 DbiExit; 

      // 

Close 

the 

BDE. 

end; 

end. 

background image

 

Rozdzial 26 Borland Database Engine (BDE) 

759

 

Jak można zauważyć kod jest dołączany do przycisku zdarzenia 

OnClick

 na 

formularzu Delphi. Kod ten można wpisać do własnego zdarzenia 

OnClick

 - by 

sprawdzić jego działanie. Należy zauważyć,  że moduł ten powoduje otwarcie 
tabeli EMPLOYEE w katalogu: 

C:\Program Files\Borland\Delphi 

3.0 \Demos\Data

. W związku z tym należy zadbać, aby tabela ta zaistniała 

przed uruchomieniem modułu.  

W klauzuli 

Uses

 modułu należy odnotować wykorzystanie modułu BDE. Zawiera 

wszystkie informacje o procedurach i typach danych dla mechanizmu bazy danych. 
Aby były one dostępne bezpośrednio dla BDE należy dodać je do polecenia 

Uses

.  

Aczkolwiek większość twórców aplikacji Delphi nigdy nie buduje aplikacji, która 
realizuje dostęp do bazy danych wyłącznie przy pomocy odwołań BDE API, 
powyższy kod traktować możemy jako pewien szablon, przy pomocy którego 
można tego dokonać.  

Dostęp do BDE z aplikacji Delphi  

Najbardziej prawdopodobny jest scenariusz zakładający, iż prawie cały dostęp do 
bazy danych realizowany będzie przy pomocy wbudowanych komponentów 
Delphi (z wykonywaniem niektórych specjalizowanych funkcji poprzez 
bezpośrednie odwołania BDE). Ilustracją tego typu podejścia jest komponent 

LiveQuery (

opisany szczegółowo w 

rozdziale 27), stosujący - w 

celu 

wykonania zapytania w zdarzeniu 

BeforeOpen

 potomka 

TTable

 - funkcję 

BDE 

DBiQExecDirect

. Kod źródłowy podano w listingu 26.2.  

Listing 26.2 Komponent LiveQuery wykorzystuje funkcję BDE 

o nazwie DBiQExecDirect 

Komponent Delphi LiveQuery 

Obsługuje edycję zbiorów wynikowych servera SQL przy pomocy

 

tymczasowych perspektyw (views). Umożliwia to uaktualnienie 

każdego modyfikowalnego zbioru wynikowego przez użytkownika, 

który stworzył go jako perspektywę na serwerze. Dlatego 

obsługiwane są wszystkie modyfikacje, które mogłyby być 

obsługiwane przez serwer za pośrednictwem perspektyw. 

 

Napisał Ken Henderson. 

 

Copyright © 1995 by Ken Henderson  

Kilka zastrzeżeń: 

 

1) Sztuczka ta jest wykonywana przy pomocy tymczasowych 

perspektyw i w 

związku z

 tym:  

background image

760 

Część IV 

a) 

Ponieważ pewne platformy, jak Sybase, nie obsługują 

tym

czasowych perspektyw, musiałem skonstruować nazwę 

tymczasową (temp name) oraz stworzyć i

 

opuścić 

perspektywę. Nazwa powstała na podstawie daty i

 czasu, 

związku z

 tym kolizje z 

innymi użytkownikami są 

praktycznie niemożliwe. Patrz kod źródłowy. Możliwa 

jest obsługa ewentualnych wyjątków. Jeśli taka sytuacja 

zdarzy się, ponownie wydawane jest polecenie Open .... 

 

b) 

Oczywiście twoi użytkownicy nadal będą potrzebowali 

zezwolenia na tworzenie perspektyw.  

c) Ponieważ komponent ten tworzy perspektywy, jest tyl

ko 

użyteczny na serwerach, które je obsługują (tj.: 

serwerach zdalnych) -

 nie można go używać z

 tabelami 

dBASE i Paradox.  

Po stronie korzyści: 

 

1) Do opisu modyfikowalnych perspektyw możemy użyć dowolnej 

składni, która jest obsługiwana przez komputer. Składnia ta 

powinna zapewniać: 

 

a) dowolną, żądaną liczbę tabel za pośrednictwem złączeń 

 

b) klauzule (clauses) 

Pozwoli to na ulokowanie całego obciążenia na serwerze, gdzie 

moim zdaniem powinno się znajdować. Oznacza to także, że 

wykonywane polecenia SQL będą najpierw kompilowane, co jest 

bardziej efektywne. Jeśli serwer nie akceptuje modyfikacji 

która ma być wykonana, wystąpi oczywiście wyjątek. 

 

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; 

background image

 

Rozdzial 26 Borland Database Engine (BDE) 

761

 

  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; 

procedure TLiveQuery.SetQuery(Value: TStrings); 
begin 
 CheckInActive; 

background image

762 

Część IV 

 SQL.Assign(Value); 
end; 

procedure TLiveQuery.CreateTemporaryView; 
var 
 

TemporaryDB : TDatabase; 

 

WorkSQL : TStrings; 

begin 
 

WorkSQL := TStringList.Create; 

 WorkSQL.AddStrings(SQL); 
 TableName:=Format(TableNameFormat,[FormatDateTime(‘yymmdd 

 

 hhnnss’,Now)]); 

 WorkSQL.Insert(0,Format(CreateViewSQL,[TableName])); 
 TemporaryDB:=Session.OpenDatabase(DatabaseName); 
 

If (TemporaryDB<>nil) 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 
try 
 WorkSQL:=TStringList.Create; 
 WorkSQL.Add(Format(DropViewSQL,[TableName])); 
 TemporaryDB:=Session.OpenDatabase(DatabaseName); 
 

If (TemporaryDB<>nil) then begin 

 

 

If (TemporaryDB.IsSQLBased) then begin 

background image

 

Rozdzial 26 Borland Database Engine (BDE) 

763

 

   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; 

procedure Register; 
begin 
 

RegisterComponents(‘Data Access’, [TLiveQuery]); 

end; 

end. 

Zwróćmy uwagę na funkcję 

DBiQExecDirect

. Zaraz po zbudowaniu procedurą 

polecenia SQL 

WorkSQL

, jest ono gotowe do przetworzenia. Z uwagi na fakt, iż 

stosowany jest komponent potomny 

Table

, nie ma prostej możliwości realizacji 

utworzonego wyrażenia SQL. Jeśli 

LiveQuery

 był potomkiem komponentu 

Query, wówczas możliwy byłby dostęp do procedury ExecSQL (ale tak nie jest). 
Tak dzieje się, gdy w 

grę wchodzi funkcja 

DBiQExecDirect

. W celu 

wykonania zapytania SQL tworzony jest tymczasowy obiekt 

hDBIDb

, przesyłany 

następnie, wraz z zawartością WorkSQL, do funkcji 

DBiQExecDirect

.  

UWAGA  

Jeśli nie osiągnięto zgodności z Serwerem SQL, wtedy - zamiast tymczasowego 
połączenia bazy danych (które zostało stworzone przez komponent) - możemy 
skorzystać z własności 

DBHandle

 komponentu 

LiveQuery

. Serwer zakazuje 

inicjowania nowego zapytania w czasie, gdy wyniki poprzedniego zapytania 
pozostają nierozstrzygnięte - stąd stosowanie 

LiveQuery.Database.Handle

 

z funkcją 

DBiQExecDirect

 w środowisku SQL Serwer nie ma uzasadnienia.  

background image

764 

Część IV 

Należy zauważyć,  że trzeci parametr funkcji 

DbiQExecDierct

 wymaga typu 

danych 

PChar

.  Łańcuchy 

Pchar

 stanowią pascalową wersję  łańcucha 

char

w C  i C++.  Począwszy od Delphi 2.0, translacja łańcuchów pascalowych na 
łańcuchy C/C++ ogranicza się do ich wpisania. Łańcuchy pascalowe są teraz 
zakończone pustym znakiem (NULL) i twórcy aplikacji także tym nie muszą się 
martwić. Co więcej - łańcuchy w Pascalu także zawierają pole długości, dzięki 
czemu nie trzeba przeszukiwać łańcucha (ze względu na pusty znak) - by odczytać 
ich długość, jak dzieje się to w przypadku języka C.  

Wykonywanie odwołań do DBMS 

BDE umożliwia również dostęp do bezpośredniej obsługi dla konkretnego DBMS. 
Taka właściwość pozwala na wykonanie odwołań do funkcji w bibliotekach 
klienta danego DBMS, z całkowitym pominięciem komponentów baz danych 
Delphi i BDE. W pewnych okolicznościach poprawi to wydajność, umożliwiając 
ponadto funkcjonalne pominięcie BDE.  

Do pobrania uchwytu połączenia rodzimego do danej DBMS należy użyć funkcji 

DbiGetProp. 

 

W tabeli 26.3 zebrano typy informacji, dostępne na każdej platformie. 

Tabela 26.3 Informacje platformy DBMS zwracane przez 

DbiGetProp.

 

Platforma  

Typ uchwytu  

Długość  

InterBase 

gds_db_handle 

Sybase 

DBPROCESS NEAR * 

Oracle 

LDA 

64 

ODBC Socket 

HDBC 

Poniższy fragment kodu zawiera pewien przykład składni, której można użyć na 
platformie Sybase - by pobrać nazwy aktualnej bazy danych:  

type  
 

DBPROCESS = Pointer;  

var  
 

Form1: TForm1;  

 

DBProc: DBPROCESS;  

 

Size DBProc: Word;  

function dbname(DBOros: DBPROCESS) : Pchar; external  

 ‘LIBSYBDB’; 

background image

 

Rozdzial 26 Borland Database Engine (BDE) 

765

 

implementation  

{$R *.DFM) 

Procedure TForm1.Button1Click(sender: Tobject);  
begin 
  

biGetProp(hDBiObj (Table1.DBHandle),dbNATIVEHNDL,  

 

 

DBProc, SizeOf(DBProc), SizeDBProc;  

 Edit1.Text:=dbname(DBProc); 
end;  

W tym przykładzie funkcja 

dbname

 z biblioteki DB-Library jest odnoszona 

zewnętrznie w 

LIBSYBDB DLL. Takie połączenie wymaga pojedynczego 

parametru bieżącego połączenia uchwytu DBPROCESS. Uchwyt ten jest 
pobierany poprzez wywołanie 

DbiGetProp i 

przekazywany następnie do 

parametru 

dbNATIVEHNDL

.  

Pobieranie informacji charakterystycznych dla danej 
platformy poprzez BDE 

Jeśli zamiarem twórcy aplikacji jest umożliwienie użytkownikowi budowania 
zapytań podczas wykonania programu, wówczas należy zapewnić sobie - na 
wybranej platformie DBMS - możliwość listowania nie tylko tabel i kolumn, ale 
także funkcji, które platforma obsługuje podczas zapytania o dane. Koniecznym 
może okazać się wyświetlanie takich elementów, jak łańcuchy, dane i funkcje 
numeryczne itd. Funkcją BDE, realizującą powyższe zadania, jest 

DbiOpenFunctionList

. Otwiera ona kursor w liście funkcji obsługiwanej 

przez skojarzoną platformę DBMS. Aby pobrać kolejno każdą obsługiwaną nazwę 
funkcji należy wywołać 

DBiGetNextRecord

. Listingi 26.3 do 26.5 ilustrują 

prosty program Delphi, wykorzystujący - do listowania tabeli obiektów obecnych 
na aktualnej platformie DBMS oraz funkcji obsługiwanych przez platformę - 
funkcję 

DbiOpenFunctionList

  

Program przedstawia listę rozwijalną aliasów BDE, które można wybrać i zapytać 
o informacje dotyczące funkcji i tabeli.  

Listing 26.3 Plik źródłowy projektu dla programu 

przykładowego funkcji BDE DbiOpenFunctionList, 

FuncEx. 

program funcex;  

uses 
 Forms, 

 funcex

∅∅

 in 'funcex

∅∅

.pas' {Form1}; 

{$R*.RES} 

background image

766 

Część IV 

begin 
 Application.Initialize; 

 

 Application.CreateForm(TForm1, 

Form1); 

 Applicatiob.Run; 
end.  

 

Listing 26.4 Plik źródłowy modułu dla FuncEx00.PAS, tylko 

moduł w

 

programie przykładowym BDE DbiOpenFunctionList, 

FuncEx. 

unit funcex00; 

interface 

uses 
 

Windows, Messages, SysUtils, Classes, Graphics, Controls, 

 

Forms, Dialogs, DBTables, StdCtrls, BDE; 

type 
 

TForm1 = class(TForm) 

  Label1: 

TLabel; 

  Label2: 

TLabel; 

  Label3: 

TLabel; 

  ListBox1: 

TListBox; 

  ComboBox1: 

TComboBox; 

  ListBox2: 

TListBox; 

  Database1: 

TDatabase; 

  procedure 

ComboBox1Change(Sender: 

TObject); 

  procedure 

FormCreate(Sender: 

TObject); 

 private 
 

 

{ Private declarations } 

 public 
 

 

{ Public declarations } 

 end; 

var 
 Form1: 

TForm1; 

implementation 

{$R *.DFM} 

procedure TForm1.ComboBox1Change(Sender: TObject); 

function Chk(ErrorValue : DBIResult) : DBIResult; 
var 
 

dbi_status : string; 

 

dbi_string : string; 

 

ErrInfo : DBIErrInfo; 

 

count : integer; 

background image

 

Rozdzial 26 Borland Database Engine (BDE) 

767

 

begin 
 dbi_status:=’’; 
 dbi_string:=’’; 
 

if (ErrorValue <> DBIERR_NONE) then begin 

  DbiGetErrorInfo(TRUE, 

ErrInfo); 

 

 

if (ErrInfo.iError = ErrorValue) then begin 

 

 

dbi_status:=’ ERROR ‘+ ErrInfo.szErrCode; 

  With 

ErrInfo 

do 

 

 

 

for count:=low(szContext) to high(szContext) do 

    if 

(strcomp(ErrInfo.szContext[count], 

 

 

 

 

 

 ‘’)<>0) then 

     dbi_status 

:= 

dbi_status+’ 

‘+ 

 

 

 

 

 

 

 ErrInfo.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; 

 

FuncInfo : DBiFUNCDESC; 

 

Counter : integer; 

begin 
 

With Database1 do begin 

  Connected:=False; 
  ListBox2.Items.Clear; 
  DatabaseName:=ComboBox1.Items[ComboBox1.ItemIndex]; 
  Open; 
  Session.GetTableNames(DatabaseName,’’,False, 

False, 

 

 

 ListBox1.Items); 

 end; 
 counter:=0; 
 

If (Chk(DbiOpenFunctionList(Database1.Handle, [ic:ccc} 

 

 fnListINCL_USER_DEF, @hcur)) = DBIERR_NONE) then begin 

 

 

if (hCur<>nil) then begin 

  while 

(DBiGetNextRecord(hCur,dbinolock,@FuncInfo, 

 

 

 nil)<>DBIERR_EOF) and (counter <50) do begin 

   ListBox2.Items.Add(FuncInfo.szName); 
   inc(counter); 
  end; 

background image

768 

Część IV 

  DbiCloseCursor(hCur); 
  ListBox2.Sorted:=True; 
 

 

end else ShowMessage(‘Error opening cursor’); 

 end; 
end; 

procedure TForm1.FormCreate(Sender: TObject); 
begin 
 Session.GetDatabaseNames(ComboBox1.Items); 
 ComboBox1.ItemIndex:=0; 
end; 

end. 

 

Listing 26.5 Plik formularza (.DFM) dla FuncEx00.PAS, 

pojedynczy moduł w

 

programie przykładowym BDE 

DbiOpenFunctionList, FuncEx. 

object Form1: TForm1 
 

Left = 200 

 

Top = 108 

 

Width = 544 

 

Height = 375 

 

Caption = ‘Form1’ 

 

Font.Color = clWindowText 

 

Font.Height = -11 

 

Font.Name = ‘MS Sans Serif’ 

 

Font.Style = [] 

 

OnCreate = FormCreate 

 

PixelsPerInch = 96 

 

TextHeight = 13 

 

object Label1: TLabel 

  Left 

33 

  Top 

  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 

background image

 

Rozdzial 26 Borland Database Engine (BDE) 

769

 

 

object ListBox1: TListBox 

  Left 

32 

  Top 

56 

  Width 

177 

  Height 

209 

  ItemHeight 

13 

  TabOrder 

 end 
 

object ComboBox1: TComboBox 

  Left 

32 

  Top 

14 

  Width 

177 

  Height 

21 

  Style 

csDropDownList 

  ItemHeight 

13 

  TabOrder 

  OnChange 

ComboBox1Change 

 end 
 

object ListBox2: TListBox 

  Left 

216 

  Top 

56 

  Width 

177 

  Height 

209 

  ItemHeight 

13 

  TabOrder 

 end 
 

object Database1: TDatabase 

  DatabaseName 

‘DBDEMOS’ 

  SessionName 

‘Default’ 

  Left 

496 

 end 
end 

background image

770 

Część IV 

Indeksy wyrażeniowe 

Jedna z największych korzyści wynikających z faktu, że BDE obsługuje indeksy 
oparte na wyrażeniach dBASE jest to, że wywodzący się z dBASE mechanizm 
obliczania wartości (ewaluator) wyrażeń możemy wykorzystać we własnych 
aplikacjach. Pozwala to na włączenie ewaluatora wyrażeń, wraz z bogactwem 
indeksów dBASE, do aplikacji Delphi. Ewaluator obsługiwałby wyrażenia 
zawierające wszystkie operacje i funkcje dostępne w indeksach dBASE.  

Aby uzyskać dostęp do ewaluatora wyrażeń BDE należy stworzyć indeks 
wyrażeniowy  w oparciu  o fikcyjną tabelę DBF dBASE. W tym celu powinniśmy 
utworzyć jednowierszową tabelę dBASE (struktura nie ma znaczenia) oraz 
umożliwić aplikacji wprowadzenie wyrażenia (które nie musi operować na tabeli). 
Tabelę  użyjemy tylko do stworzenia indeksu - by obliczyć wyrażenie. Gdy 
wyrażenie jest wprowadzane do aplikacji, należy stworzyć indeksowanie tabeli na 
bazie wprowadzonego wyrażenia (jako klucza). Wystarczy wówczas przełączyć 
nowy indeks tak, aby stał się bieżącym indeksem tabeli i - wykorzystując funkcję 
BDE 

DbiExtractKey

 - powrócić do głównej (lub obliczonej) wartości klucza.. 

Poniżej przedstawiliśmy fragment kodu z aplikacji stosującej opisaną technikę.  

procedure TForm1.Button1Click(Sender: TObject); 
var 
 

IndexDesc : IDXDesc;  

 

KeyString : String;  

begin  

 

Rysunek 26.1 
Aplikacja FuncEX 
pokazuje tabele 
i funkcje dostępne 
na platformie 
DBMS.  

background image

 

Rozdzial 26 Borland Database Engine (BDE) 

771

 

 

With Table1 do begin  

 

 

If not Active then Open;  

  AddIndex('EXPEVAL',Edit.Text,[ixExpression]); 
  Close 

 

  IndexName:='EXPEVAL'; 
  Open; 

 

  DbiGetIndexDesc(Table1.Handle,

,IndexDesc);  

  SetLenght(KeyString,IndexDesc.iKeyLen); 
  DbiExtractKey(Tablee1.Handle, 

nil, 

 

 

 

 PChar(KeyString)); 

  Edit2.Text:=KeyString; 

 

  Close; 
  DeleteIndex('EXPEVAL'); 
  IndexName:='DUMMY'; 
 end; 

 

end; 

Najważniejsza jest tutaj procedura 

DbiExtractKey

, która zwraca poddaną 

translacji lub wyliczoną - na podstawie indeksu. - wartość klucza Ta pożyteczna 
właściwość umożliwia dodanie do skompilowanej aplikacji Delphi 
przypominającego interpreter narzędzia, pozwalającego wyliczyć wartość 
wyrażenia. Na rysunku 26.2 przedstawiono aplikację, wykorzystującą funkcję 

DbiExtract

 do obliczenia wyrażenia złożonego.  

 

Rysunek 26.2. 
Aplikacja 
z mechanizmem 
kalkulacji wyrażeń 
- funkcjąi BDE 
DbiExtractKey  

background image

772 

Część IV 

Optymalizacja BDE 

Optymalizacja BDE może być trudnym procesem. Wskazówki podane poniżej nie 
stanowią "cudownego leku", ale pozwalają ocenić, czy mechanizm jest 
dostatecznie szybki (i czy nie wymaga dodatkowej kosmetyki).  

„Modyfikacje danych są spowalniane przez liczne indeksy zewnętrzne. 

W związku z tym należy ograniczyć je do niezbędnych. Może okazać się,  że 
będzie szybciej opuścić indeksy i przebudować je po wstawieniu danych.  

„Tabele powinniśmy otwierać jedynie w trybie wyłącznego dostępu. 

„W miarę możliwości unikać manipulowania wierszami - lepiej operować na 

porcjach BDE.  

„Stosując funkcję 

DbiWriteBlock

 powinniśmy starać się pracować 

z blokami fizycznymi o różnej wielkości.  

„Funkcji 

DbiAcqPersistTableLock

 można użyć w stosunku fikcyjnej 

tabeli - by wymusić stworzenie pliku .

LCK

, który - przy otwieraniu i zamykaniu 

tabel - nie byłby kasowany i ponownie tworzony w trakcie otwierania 
i zamykania tabel.  

„Należy używać tabel tworzonych w pamięci operacyjnej, a nie na dysku 

twardym. Nie wolno nam jednak zapominać o tym, iż tabel przechowywanych 
w pamięci operacyjnej nie można indeksować, kopiować lub zapisywać 
w sposób trwały.  

Optymalizacja dostępu BDE do SQL  

Optymalizacja SQL-a została omówiona dokładniej  w rozdziale  24  i 25.  W tym 
miejscu ograniczyliśmy się do kilku ogólnych wskazówek związanych z dostępem 
BDE do SQL.  

„Wykonując serię modyfikacji na serwerze SQL, należy stosować transakcje 

jawne, unikając rozpoczynania i kończenia transakcji niejawnych dla każdej 
modyfikacji.  

„Przygotować  złożone zapytania i procedury pamiętane dla bezpośrednich 

odwołań SQL (PassThrough SQL). W rezultacie BDE dostarczy, możliwie 
najszybciej, nasze zapytania SQL do serwera, pozwalając nam na stosowanie 
dowolnego SQL, wspieranego przez mechanizmy serwera.  

„Jak już wspominaliśmy, istnieje możliwość - dzięki informacjom przekazanym 

przez funkcję IDAPI 

DbiGetProps

 - realizacji wywołań rodzimych dla 

DBM. 

background image

 

Rozdzial 26 Borland Database Engine (BDE) 

773

 

„Wykonać pierwszy człon zapytania, co pozwoli zmniejszyć jego zbiór 

wynikowy - a po zredukowaniu (w maksymalnym stopniu) zestawu wyników - 
grupowanie lub złączenie . 

„W pewnych przypadkach zalecane jest kopiowanie wyników zapytania do 

lokalnej tabeli.  

„Funkcje 

DbiAddFilter

DbiSetRange

 i 

DbiSetFieldMap

 pomagają 

zredukować ilość aktualnie dostępnych danych. Efektem każdorazowej redukcji 
danych pierwotnych (przeznaczonych do przetwarzania) będzie przyspieszenie 
pracy aplikacji. Zauważmy,  że wszystkie te właściwości obsługiwane są 
bezpośrednio przez kontrolki Delphi DataSet  

„Tworzenie indeksów zstępujących dla tabel, na których wykonywane jest 

często przewijanie wsteczne, pozwala istotnie zaoszczędzić czas pracy serwera. 

„Gdy znajdujemy się w środku dużej tabeli lub jeśli tabela zawiera indeks 

złożony, powinniśmy unikać stosowania 

DbiSetToEnd

 i 

DbiSetToKey. 

Wywołanie tych funkcji w nieodpowiednim momencie może spowodować 
sekwencyjne przeglądanie przez aplikację znacznej liczbę wierszy.