Uzamykání
Uzamykací prostředí BDE je hierarchie obsahující tři vrstvy:
-
Vrstva sezení - Vlastní madla databází, kurzory tabulek,
získané zámky tabulek a zámky záznamů.
-
Vrstva madla databáze - Omezení režimu otevírání tabulek
v databázi.
-
Vrstva kurzoru tabulky - Režim otevření kurzoru může
omezit přístup.
Vrstva sezení
Na vrcholu hierarchie uzamykání BDE je vrstva sezení. Sezení
nepřímo řídí některé zámky neboť řídí zdroje včetně madel databází a kurzorů
tabulek. V jednom sezení může být otevřeno více madel databází; to nastává,
když aplikace přistupuje najednou k různým databázím. Když sezení je uzavřeno,
pak všechny zdroje přiřazené k sezení jsou uzavřeny a všechny zámky vlastněné
těmito zdroji jsou uvolněny.
Sezení přímo vlastní zámky tabulek a zámky záznamů získané
aplikací po otevření tabulky. To znamená, že pokud na jedné tabulce v sezení
je otevřeno více kurzorů, pak jeden kurzor může uvolnit zámek získaný jiným
kurzorem. Sezení je kompletně izolované od ostatních sezení.
Vrstva madla databáze
O jeden krok níže v hierarchii uzamykání BDE je vrstva madla
databáze. I když žádné zámky nejsou vlastněny madlem databáze, režim sdílení
přiřazený k databázi při jejím otevření určuje zda tabulky v databázi mohou
být otevírány výlučně nebo sdíleně. Pokud databáze je otevřena ve výlučném
režimu, pak všechny tabulky mohou být otevřeny pouze ve výlučném režimu.
Když databáze je uzavřena, pak všechny zdroje alokované
na madlo databáze jsou uvolněny, včetně kurzorů tabulek a zámků tabulek
vlastněných těmito kurzory.
Vrstva kurzoru tabulky
Dole v hierarchii uzamykání BDE je vrstva kurzoru. Pouze
zámky umístěné na tabulce, když je otevřena funkcí DbiOpenTable
jsou vlastněny kurzorem. Když tabulka je otevřena ve výlučném režimu, pak
k tabulce nemohou přistupovat další uživatelé. Exklusivní zámek zabraňuje
ostatním uživatelům v přístupu k tabulce a v umístění nějakého zámku na
ní. Pokud tabulka je otevřena ve sdíleném režimu, pak ostatní kurzory mohou
přistupovat k tabulce a mohou na ní zřizovat čtecí nebo zápisové zámky.
Když je kurzor uzamčen, pak všechny exklusivní zámky
umístěné na tabulce při jejím otevření jsou uvolněny.
Získané zámky
Všechny zámky získané po otevření tabulky jsou vlastněné
sezením (a ne kurzorem). Je několik typů získaných zámků:
-
Získané zámky tabulky
-
Získané trvalé zámky tabulky
-
Záznamové zámky
K testování stavu získaných zámků tabulky použijeme DbiIsTableLocked.
Aplikace specifikuje typ zámku (nezamčen, čtecí zámek nebo zápisový zámek)
a funkce vrací počet zámků tohoto typu umístěných na tabulce.
Pro tabulky dBASE, FoxPro, Access a Paradox, k testování
zda tabulka je fyzicky sdílena na síti nebo lokálním zařízení a otevřena
ve sdíleném režimu, použijeme BbiIsTableShared. Pro tabulky SQL
tato funkce může být použita k testování zda tabulka byla otevřena ve sdíleném
režimu.
Pokud aplikace potřebuje umístit zámek na tabulku, která
byla otevřena ve sdíleném režimu, pak volá funkci BDE DbiAcqTableLock.
Pokud zámek nemůže být získán je vrácena chyba. DbiAcqTableLock
může umístit na tabulku čtecí nebo zápisový zámek.
Zápisový zámek zabraňuje ostatním uživatelům v aktualizaci
tabulky. Na tabulce může být umístěn pouze jeden zápisový zámek. Čtecí
zámek zabraňuje všem v aktualizaci tabulky a zabraňuje ostatním uživatelům
v umístění zápisového zámku na tabulku (data v průběhu čtení nemohou být
změněna). Může existovat více čtecích zámků.
Pokud ovladač nepodporuje čtecí zámky, pak čtecí zámky
jsou nahrazeny zápisovými. Např. pro tabulky dBASE jsou čtecí zámky nahrazeny
zápisovými. Pro tabulky SQL, zápisové zámky jsou stejné jako čtecí a chování
se může lišit v závislosti na serveru. Tabulka může získat více než jeden
zámek.
K uvolnění zámku tabulkové úrovně umístěným DbiAcqTableLock
voláme DbiRelTableLock. Každý získaný zámek vyžaduje uvolnění samostatným
voláním DbiRelTableLock.
Trvalý zámek může být umístěn před vytvářením tabulky.
Pro tabulky Paradoxu tato služba může být použita k rezervování jména tabulky
pro budoucí použití. Pro tabulky SQL, BDE si pamatuje, že zámek byl umístěn
a když tabulka je vytvářena během připojení, pak tabulka je uzamčena. Tyto
zámky jsou získávány funkcí DbiAcqPersistTableLock. Pro uvolnění
trvalých získaných zámků tabulky použijeme DbiRelPersistTableLock.
Aplikace může přidělovat záznamové zámky během získávání
záznamů. Většina funkcí BDE, které umožňují získávat záznam poskytují uzavírací
volbu; např.
DbiGetNextRecord,
DbiGetPriorRecord a DbiGetRelativeRecord.
Parametr eLock může být použit ke specifikaci některého z následujících
záznamových zámků:
Nastavení |
Popis |
dbiNOLOCK |
Bez zámku, umožňuje ostatním uživatelům číst, aktualizovat
a uzamykat záznam. |
dbiREADLOCK |
Změněno na zápisový zámek. |
dbiWRITELOCK |
Umožňuje ostatním uživatelům číst záznam, ale zabraňuje
v aktualizaci záznamu nebo v umístění zámku na záznam. |
Správce uzamykání Paradoxu a dBASE nahrazuje čtecí zámky
zápisovými zámky; tedy záznam je nebo není uzamčen.
Protože některé funkce získávání záznamů BDE provádějí
jiné operace než uzamykání, pořadí ve kterém tyto operace se vyskytnou,
může být důležité:
-
Nejprve nastává přesun kurzoru.
-
Ovladače Paradoxu a dBASE se pokusí uzamknout záznam před
plněním vyrovnávací paměti záznamu.
-
Ovladače SQL plní vyrovnávací paměť záznamu klienta a pak
se pokusí uzamknout záznam.
Přesun kurzoru nastává i když uzamčení je neúspěšné. Např.
pokud DbiGetNextRecord je volán se čtecím zámkem, pak kurzor se
přesune na následující záznam, záznam je pak uzamčen. Když ale záznam je
již ale uzamčen jiným uživatelem, pak pokus o uzamčení je neúspěšné, ale
kurzor má změněnou pozici.
K testování stavu uzamčení záznamu použijeme DbiIsRecordLocked.
Tato funkce vrací stav uzamčení současného záznamu (uzamčen nebo neuzamčen).
Aplikace může volat funkci DbiRelRecordLock k
uvolnění záznamového zámku na současném záznamu nebo uvolnění všech záznamových
zámků získaných v současném sezení. Dále DbiModifyRecord poskytuje
volbu k uvolnění zámku pro dokončení operace.
Koexistence zámků tabulky
Každý typ zámku tabulkové úrovně umístěný na tabulku ovlivňuje
nějaký stupeň přístupu ostatních uživatelů k tabulce. Můžeme použít zámek
agresivně k zabránění ostatním uživatelům v přístupu k tabulce, nebo použít
zámek defensivně k zabránění ostatním uživatelům umisťovat zámky omezující
naši aplikaci přistupovat k tabulce. Následující tabulka zobrazuje výsledek
pokusu uživatele 2 umístit tabulkový zámek po úspěšném umístění zámku uživatelem
1.
|
Uživatel 2 |
|
|
|
|
Pokus o otevření tabulky ve výlučném režimu. |
Pokus o získání záznamového zámku |
Pokus o získání čtecího zámku |
Pokus o otevření tabulky ve sdíleném režimu. |
Uživatel 1
Pokus o otevření tabulky ve výlučném režimu. |
neúspěch |
neúspěch |
neúspěch |
neúspěch |
Pokus o získání záznamového zámku |
neúspěch |
neúspěch |
neúspěch |
úspěch |
Pokus o získání čtecího zámku |
neúspěch |
neúspěch |
úspěch pro Paradox, neúspěch pro dBASE nebo FoxPro |
úspěch |
Pokus o otevření tabulky ve sdíleném režimu. |
neúspěch |
úspěch |
úspěch |
úspěch |
Strategie uzamykání
Při volbě uzamykají strategie se musíme zabývat jak aplikace
potřebuje chránit data před ostatními uživateli a jak data jsou chráněna
ostatními uživateli. Každý zpřístupňovaný systém databáze může mít také
různá pravidla používaná správcem uzamykání. SQL používá jiná pravidla
než Paradox a dBASE.
Specifikace SQL chování uzamykání
V dBASE, Paradoxu, FoxPro a Access záznamové zámky zabraňují
jiným uživatelům aktualizovat záznam. SQL ale pracuje se záznamovými zámky
jiným způsobem. Pokud záznam v tabulce SQL není v záznamové cache, pak
záznam je získáván ze serveru. Klient má lokální kopii záznamu, ale tato
kopie nemusí být aktuální, když jiný klient získá stejný záznam ze serveru
a modifikuje nebo jej zruší dříve než první klient se pokusí odeslat změny.
Ovladače SQL (a některé ODBC) používají optimistické
uzamykání. Optimistický zámek umožňuje aktualizaci uzamčeného zámku jiným
uživatelem, ale když aplikace, která umístila zámek se pokusí modifikovat
záznam, pak BDE oznámí aplikaci že záznam byl změněn a že požadované operace
nemůže být provedena protože někdo změnil data. Aplikace pak zvolí, zda
prozkoumá nový záznam a určí zda svoji změnu má ještě provést.
Transakce
Systémy SQL používají zpracování transakcí s commit
a rollback které celou řadu operací v transakci udělají trvalými
nebo ji celou zruší. Transakce mohou být prováděny na všech platformách
SQL podporovaných BDE. Transakce je řada programových příkazů, které přistupují
k datům v naši databázi. Když transakce je dokončena, pak celou transakci
zapíšeme nebo zrušíme. Při zápisu se všechny změny provedené v transakci
stanou trvalými.
V připojení k SQL databázi může být aktivní pouze jedna
transakce. Jakýkoliv pokus o spuštění další transakce před dokončením první
způsobí chybu.
Operace SQL vždy umisťujeme do kontextu transakce. Když
explicitní transakce nenastávají, pak ovladač SQL zpracovává transakce
serveru SQL transparentně pro klienta. Všechny úspěšné modifikace dat serveru
SQL se bezprostředně stávají trvalými.
K zahájení transakce se používá funkce DbiBeginTran.
Po úspěšném volání DbiBeginTran se transakce stává aktivní. Aplikace
specifikuje úroveň izolací použitou pro transakci když DbiBeginTran
je volána. Možné hodnoty jsou:
-
xilDIRTYREAD: jsou čteny změny, které nejsou trvalými.
-
xilREADCOMMITTED: trvalé změny ostatních transakcí
mohou být čteny.
-
xilREPEATABLEREAD: změny ostatních transakcí na předem
přečtená data nejsou viditelné.
Schopnosti a chování izolací a možností opakovaného čtení
závisí na SQL serveru.
DbiEndTran končí transakci. Aplikace specifikuje
typ konce transakce. Možné hodnoty jsou:
-
xendCOMMIT: Trvalý zápis transakce.
-
xendCOMMITKEEP: Pro některé ovladače SQL, trvalý zápis
transakce a držení kurzoru.
-
xendABORT: Zrušení transakce.
Kurzory BDE mohou zůstat aktivní i když přiřazené kurzory
SQL jsou uzavřeny. BDE spravuje opětovné otevření kurzorů serveru SQL transparentně.
xendCommit a xendABORT drží kurzory, pokud
ovladač a databáze podporují držení kurzorů. Pokud databáze nepodporuje
držení kurzoru, pak existují čtyři možnosti pro každý otevřený kurzor na
chování uživatele BDE:
-
Kurzor pro otevřený dotaz se získaným výsledkem je uložen
lokálně. Je možno číst data.
-
Kurzor otevřený na tabulce podporující přímé umisťování je
uzavřen. Není ovlivněno žádné chování.
-
Kurzor otevřený na tabulce, která nepodporuje přímé umisťování
je původně otevřen v jiné transakci nebo kontextu připojení (pokud to databáze
podporuje). Tento kurzor zůstává otevřen protože existuje v jiném kontextu
z požadované transakce.
-
Není-li splněno nic z předchozího, pak kurzor je uzavřen
a další přístup k objektům BDE přiřazeným ke kurzoru vrací chybu.
Transakce na Paradoxu, dBASE, FoxPro a Access
Transakce pro ovladače Paradoxu, dBASE, FoxPro a Access (lokální
transakce) umožňují zrušit nebo zapsat změny do standardních tabulek. To
umožňuje, aby aplikace prováděla aktualizace konzistentním způsobem. Když
lokální transakce je spuštěna na standardní databázi, pak aktualizace prováděné
nad tabulkami v této databázi jsou zaznamenány. Každý záznam obsahuje původní
obsah aktualizovaného záznamu. Když transakce je aktivní, pak aktualizované
záznamy jsou uzamčeny a tyto zámky jsou drženy dokud transakce není ukončena:
-
Operací COMMIT (uvolňuje všechny zámky držené při aktivní
transakci).
-
Operací ROLLBACK (obnovuje původní stav záznamu a potom uvolňuje
zámky).
Transakce a odložené aktualizace
Když transakce jsou aktivní, pak aktualizace jsou bezprostředně
zaslány připojeným tabulkám. Chyby jsou bezprostředně oznamovány klientům.
Protože aktualizace jsou bezprostředně zasílány připojeným tabulkám, pak
aktualizace jsou viditelné ostatními transakcemi. A protože každý modifikovaný
záznam je uzamčen, ostatní uživatelé jej nemohou změnit.
Toto chování se liší od chování odložených aktualizací,
kdy aktualizace nejsou zasílány do připojených tabulek dokud nenastane
čas zápisu. Tedy chyby nejsou oznamovány dokud nenastane čas zápisu. Zámky
jsou drženy pouze v průběhu zápisu. Pokud v průběhu zápisu vznikne chyba,
pak klient může zrušit proces zápisu. Pokud klient zruší proces zápisu,
pak je obnoven původní stav tabulek.
Hlavní výhodou služby odložených aktualizací je to, že
zámky jsou drženy pouze v průběhu zápisu, a tedy není omezován přístupový
čas serveru SQL pro ostatní transakce, ale umožňují jiným uživatelům měnit
data. Výhody a nevýhody jsou shrnuty v následující tabulce.
|
Výhody |
Nevýhody |
Transakce |
Aktualizace jsou bezprostředně zasílány tabulkám. Modifikované
záznamy jsou viditelné ostatními uživateli. Modifikované záznamy jsou uzamčeny.
Chyby jsou oznamovány okamžitě. |
Uzamčení pro ostatní uživatele, dokud záznam je modifikován.
Lokální transakce mají omezení maximálního počtu uzamčených záznamů. |
Odložené aktualizace |
Zámky jsou drženy pouze v průběhu zápisu (neomezuje v
přístupu ostatní transakce). Odložené aktualizace mohou být použity s libovolným
kurzorem v tabulce. Nepřekračujeme omezení počtu zámků pro lokální transakce. |
Umožňuje ostatním uživatelům modifikaci záznamů. Pokud
chyba vznikne v průběhu zápisu, a vrátíme tabulku do původního stavu, pak
všechny modifikace jsou ztraceny. |
Stupně izolace transakcí
Stupeň izolace poskytnutý transakcemi na standardních databázích
je stupeň 0. To znamená, že transakce nepřepisuje nečistá data jiné transakce.
Protože je podporován pouze stupeň izolace 0, transakce na standardních
databázích jsou subjektem následujících omezení:
-
Možné ztráty aktualizací - Dvě transakce provádějící
čtení bez uzamykání záznamů, tj. použitím protokolu dbiNOLOCK. Jestliže
tyto dvě transakce odesílají své aktualizace bezprostředně, konečný výsledek
zahrnuje pouze změny jedné transakce a ztrátu aktualizací druhé transakce.
-
Transakce neizoluje od nečistého čtení - Transakce
T1 může číst záznam předem aktualizovaný jinou transakcí T2 a modifikuje
tento záznam. Čtení záznamu T1 může být nekonzistentní, protože není nakonec
aktualizovaný T2. Tedy čtení transakce T1 je nečisté čtení.
-
Nechrání před neopakovatelným čtením - Transakce T1
čte záznam dvakrát, jednou předtím než transakce T2 záznam aktualizuje
a podruhé po trvalém zápisu aktualizací T2. Dvě operace čtení vrací různé
hodnoty záznamu a první čtení není opakovatelné.
Použitím příslušného uzamykacího mechanismu v průběhu aktualizací,
klient může poskytnout vyšší stupeň izolace transakcí. Např. ztracené aktualizace
mohou být zachráněny pokud transakce vždy získávají čtecí zámky na modifikované
záznamy. Funkce dbiBeginTran podporuje několik úrovní izolací transakcí:
xilDIRTYREAD,
xilREADCOMMITTED
a xilREPEATABLEREAD.
Pro tabulky SQL, příslušné úrovně izolací transakcí,
mohou být požadovány dople možností serveru SQL. Úroveň izolace xilREADCOMMITTED
zachraňuje ztrátu aktualizací a nečistému čtení. Úroveň izolací xilREPEATABLEREAD
zabraňuje neopakovatelnému čtení.
Protože všechny aktualizace jsou atomické, uživatelé
budou informováni o konfliktech uzamykání bezprostředně. Detekce uváznutí
není prováděna. Uváznutí nastane, když dvě transakce čekají na zámky držené
navzájem. Pokud je libovolný konflikt uzamykání mezi různými transakcemi,
pak klientovi je vrácena chybová zpráva. Když uváznutí nastane, pak klient
může rozhodnout o zrušení transakce.
Použití transakcí
BDE poskytuje dvě API funkce: DbiBeginTran (zahájení
transakce) a DbiEndTran (ukončení transakce):
DBIResult DBIFN DbiBeginTran ( // Zahájení
transakce
hDBIDb
hDb, // Madlo databáze
eXILType
eXIL, // Úroveň izolace transakcí
phDBIXact
phXact // Vraceno. Madlo Xact
);
DBIResult DBIFN DbiEndTran (
// Konec transakce
hDBIDb
hDb, // Madlo databáze
hDBIXact
hXact, // Madlo Xact
eXEnd
eEnd // Typ ukončení transakce
);
typedef enum
// Úrovně izolace transakcí
{
xilDIRTYREAD,
// Čtení netrvalých změn
xilREADCOMMITTED,
// Trvalé změny, bez zjevování
xilREPEATABLEREAD
// Plná opakovatelnost čtení
} eXILType;
typedef enum
// Konec řízení transakce
{
xendCOMMIT,
// Trvalý zápis transakce
xendCOMMITKEEP,
// Trvalý zápis transakce, držení kurzorů
xendABORT
// Zrušení transakce
} eXEnd;
Následující výsledky nastanou když jsou aktivní transakce:
-
Pokud jsou aktivní transakce v sezení, pak DbiCloseSession
uzavírá toto sezení a jeho aktivní transakce jsou zrušeny. Podobně DbiExit
ruší aktivní transakce v systému.
-
V případě standardních databází (lokální transakce), DbiModifyRecord,
DbiInsertRecord
a DbiDeleteRecord jsou používány k provádění záznamu transakcí.
Deník je udržován, pokud transakce je aktivní. Je zrušen po trvalém zápisu
nebo zrušení transakce.
Dotazování databází
API BDE umožňuje, aby klient používal SQL nebo QBE pro přístup
k tabulkám dBASE, FoxPro, Access a Paradoxu stejně jako k SQL tabulkám.
Skupina funkcí rozhraní dotazu BDE je poskytnuta pro
předávání dotazů SQL nebo QBE na serverové i na PC založené zdroje.
Dotazování Paradoxu, dBASE, FoxPro a Access
Společný modul dotazů umožňuje vývojářům BDE aplikací přistupovat
k tabulkám ve standardních databázích pomocí jazyků SQL nebo QBE. Ve standardních
databázích jsou podporovány dvě kategorie příkazů SQL (Lokálního SQL):
-
Jazyk definice dat (DDL)
-
Jazyk manipulace dat (DML)
Když zapisujeme příkazy SQL pro použití na tabulkách dBASE,
FoxPro, Access a Paradoxu je nutno dodržovat následující pojmenovací konvence:
Jména tabulek, které obsahují tečku musí být uvedeny
v uvozovkách nebo apostrofech. Např.
select * from 'c:\sample.dat\table'
select * from "table.dbf"
Jména tabulek mohou obsahovat přezdívky stylu BDE. Např.
select * from ":data:table"
Jména, která jsou klíčovými slovy musí být uvedeny v
uvozovkách. Např.
select passid from "password"
Jména, která obsahují mezeru musí být uvedeny v uvozovkách.
Např.
select * from "old table"
Jména položek, které mají mezeru musí být zapsány v uvozovkách.
Např.
select e."Emp Id" from Employee e
Jména položek, která jsou klíčovými slovy musí být v
uvozovkách. Např.
select t."date" from Table t
Jména tabulek zapsaná v uvozovkách musí používat korelační
jména.
V DML jsou podporovány následující klauzule: SELECT,
WHERE,
ORDER
BY, GROUP BY, UNION a HAVING a je možno používat
následující agregační funkce: SUM, AVG,
MIN,
MAX
a COUNT. Typ položky vracený agregačními funkcemi je
DOUBLE.
Jsou podporovány následující operátory: +, -, *, /, =, <, >, <>,
<=, >=, NOT. Operace UPDATE,
INSERT a
DELETE
jsou plně podporovány na úrovni vstupu SQL 92. Např.
DELETE FROM "Current Cust.db" C
WHERE C."CustID" IN
(SELECT O."CustID"
FROM "Old Cust.db" O)
Dále je podporováno:
-
Poddotazy v klauzulích SELECT, WHERE a HAVING.
Mimo skalárních relačních operátorů ( =, <, > ... ), jsou podporovány
i IN,
ANY,
SOME,
ALL a EXISTS.
-
Jsou podporovány agregační výrazy. Např.
SUM( Field * 10 )
SUM( Field ) * 10
SUM( Field1 + Field2 )
-
Konstrukce typu SUM( MIN(Field) ) jsou podporovány.
-
Můžeme omezit aktualizovatelnost libovolného dotazu nastavením
vlastnosti
stmtCONSTRAINED na true před provedením. Při pokusu
o modifikaci nebo vložení bude signalizována chyba.
Syntaxe DDL pro tabulky Paradoxu, dBASE, FoxPro a Access
je omezena na CREATE TABLE (nebo INDEX) a DROP TABLE
(nebo INDEX). Např.
create table parts ( part_no char(6), part_name
char(20) )
Následující příklad ukazuje jak DDL SQL může být prováděno
prostřednictvím BDE:
hDBICur hCur;
pBYTE szQuery = "create table 'c:\\example\\test.dbf'
"
"( fld1
int, fld2 date)";
rslt = DbiQExecDirect(hDb, langSQL, szQuery,
&hCur);
Dotazování různých databází
Prostřednictvím rozhraní BDE, vývojář aplikace může používat
SQL ke spojování tabulek v různých datových zdrojích. Následující příkaz
SQL ukazuje spojení tří tabulek z různých platforem pomocí přezdívek:
select distinct c.cust_no, c.state, o.order_no,
i.price
from ':Local_alias:customer.db' c,
':IB_alias:order'
o,
':SYB_alias:lineitem'
i
where o.cust_no = c.cust_no and o.order_no
= i.order_no
Přímé provádění dotazů
Pro jednoduché dotazy, kde není nutná speciální příprava
použijeme DbiQExecDirec. Tato funkce bezprostředně připraví a provede
dotaz SQL nebo QBE a vrací kurzor na výsledkovou množinu, pokud je generována.
Aplikace předává madlo databáze, specifikuje zda jazyk dotazu je SQL nebo
QBE a předává řetězec dotazu.
S jazykem dotazu SQL, pokud specifikované madlo databáze
ukazuje na serverovou databázi, pak je očekáván přirozený dialekt serveru
SQL. Pokud madlo databáze referuje na standardní databázi, pak příkaz SQL
je omezen na podporovanou podmnožinu společným modulem dotazu.
Následující příklad ukazuje jak dotaz SQL je prováděn
funkcí DbiQExecDirect:
DBIResult rslt;
hDBICur hCur;
pBYTE szQuery =
"Select t.name, t.age "
"from EMPLOYEE t "
"where t.age > 30 "
"and t.salary > 1000000 ";
rslt = DbiQExecDirect(hDb, qrylangSQL, szQuery,
&hCur);
Postupné provádění dotazů
Některé dotazy vyžadují příkazové madlo a vyžadují postupné
provádění. Příkazové madlo je vyžadováno pokud aplikace potřebuje řídit
typ tabulky ve výsledkové množině nebo ke spojování s parametry pro dotazy.
Pro každý stav dotazu je používána samostatná funkce:
-
K získání nového příkazového madla, voláme DbiQAlloc.
-
Ke změně vlastností v příkazovém madlu voláme DbiSetProp.
Zde je také možno určit, zda chceme "živou" výsledkovou množinu, tj. modifikovatelnou.
-
Pro přípravu dotazu voláme DbiQPrepare.
-
Pro provedení připraveného dotazu voláme DbiQExec.
-
Pro uvolnění zdrojů spojených s dotazem voláme DbiQFree.
Funkce DbiQAlloc alokuje příkazové madlo vyžadované
funkcemi přípravy dotazu. Specifikujeme madlo databáze a jazyk dotazu a
je vráceno příkazové madlo. DbiQAlloc je nezbytným prvním krokem
ve všech připravovaných dotazech.
DbiSetProp je použita k nastavení vlastností objektu
na specifikované hodnoty. V tomto případě objekt je příkazové madlo vrácené
DbiQAlloc.
Nastavované vlastnosti mohou být typ výsledkové tabulky, stupeň živosti
nebo režim dotazu pro spojování parametrů. Následující příklady ukazují
jak nastavovat tyto vlastnosti:
DbiSetProp(hStmt, stmtANSTYPE, (UINT32) szPARADOX);
DbiSetProp(hStmt, stmtLIVENESS, (UINT32)
wantLIVE);
Funkce DbiQPrepare je použita pro přípravu dotazu
pro následující provádění. Akceptuje madlo příkazu obsahující připravovaný
dotaz.
Předcházející příklad ukazuje jak můžeme specifikovat
naše preference pro živou nebo zrušitelnou výsledkovou množinu v průběhu
provádění dotazu. Zrušitelná výsledková množina se podobá kopii původních
dat vybraných dotazem. Živá výsledková množina je pohledem na původní data
a pokud živou výsledkovou množinu modifikujeme, pak změny se projeví na
původních datech.
Když specifikujeme naše preference na živou výsledkovou
množinu, pak Správce dotazu se pokusí nám poskytnou živou výsledkovou množinu.
Někdy to ale není možné. Po provedení dotazu a návratu výsledkové množiny
můžeme zjistit, zda se jedná o živou množinu prozkoumáním vlastnosti bTempTable
kurzoru. Je-li tato vlastnost false, pak se jedná o živou výsledkovou
množinu.
Dotazy SQL na servery SQL vracejí chybu, pokud výsledek
nemůže být živý. bTempTable je přípustná pro lokální dotazy.
Možné hodnoty pro životnost jsou:
Hodnota |
Popis |
wantCANNED |
Zrušitelná výsledková množina |
wantLIVE |
Živá výsledková množina |
wantSPEED |
Rozhoduje Správce dotazu (na základě rychlejší metody) |
wantDEFAULT |
Stejné jako wantCANNED |
DbiQExec provádí předem připravený dotaz identifikovaný
předaným příkazovým madlem a vrací kurzor na výsledkovou množinu (je-li
generována). Pro všechny dotazy (lokální i vzdálené) připravený dotaz může
být proveden několikrát, ale pouze když nevyřízené výsledky byly přečteny
nebo zrušeny (pomocí DbiCloseCursor).
Funkce DbiQFree je vždy použita jako poslední
krok v provádění připravovaných dotazů k uvolnění všech systémových zdrojů
alokovaných v průběhu a použití dotazu.
Získávání a nastavování vlastností
Každý objekt BDE je definován množinou vlastností. Vlastnosti
definující objekt závisí na typu objektu. Např. session je objekt
BDE a jeho vlastnosti zahrnují sesMAXPROPS, sesSESSIONNAME
a sesCFGMODE2. Každý typ objektu má své vlastní vlastnosti. Hodnoty
jsou při vytváření objektu přiřazeny k vlastnostem. Např. jméno tabulky
je přiřazeno vlastnosti curTABLENAME objektu kurzoru, když tabulka
je otevřena pomocí DbiOpenTable.
Hodnoty některých vlastností mohou být změněny funkcí
DbiSetProp.
K nastavení vlastnosti, aplikace předává madlo objektu, jméno měněné vlastnosti
a novou hodnotu vlastnosti. K získání současné hodnoty vlastnosti použijeme
DbiGetProp.
K získání madla objektu použijeme DbiGetObjFromName. K získání madla
databáze kurzoru použijeme
DbiGetObjFromObj.
Následující příklad ukazuje metody pro získávání jména/typu
tabulky, když vše co je dostupné je kurzor tabulky.
UINT16 iLen;
DBITBLNAME tblName;
DBINAME tblType,
dbName;
// Kurzor tabulky umožňuje přistup ke jménu
a typu tabulky
DbiGetProp(hCursor, curTABLENAME, (pVOID)
tblName,
sizeof(tblName), &iLen);
DbiGetProp(hCursor, curTABLETYPE, (pVOID)
tblType,
sizeof(tblType), &iLen);
// Můžeme také přistupovat k vlastnostem
databáze (jako je
// jméno databáze přiřazené ke kurzoru).
DbiGetProp(hCursor, dbDATABASENAME, (pVOID)
dbName,
sizeof(dbName), &iLen);
Vlastnosti objektů
Každý objekt je definován svoji vlastní množinou vlastností
jak je uvedeno v následující tabulce. Ne všechny ovladače podporují všechny
vlastnosti.
Vlastnost
|
System
|
Session
|
Database
|
Driver
|
Cursor
|
Statement
|
sysMAXPROPS |
x
|
x
|
x
|
x
|
x
|
x
|
sysLOWMEMUSAGE |
x
|
x
|
x
|
x
|
x
|
x
|
sesMAXPROPS |
|
x
|
x
|
|
x
|
x
|
sesSESSIONNAME |
|
x
|
x
|
|
x
|
x
|
sesNETFILE |
|
x
|
x
|
|
x
|
x
|
sesCFGNAME |
|
x
|
x
|
|
x
|
x
|
sesCFGUPDATE |
|
x
|
x
|
|
x
|
x
|
sesCFGMODE2 |
|
x
|
x
|
|
x
|
x
|
dbBATCHCOUNT |
|
|
x
|
|
|
|
dbBLOBCOUNT |
|
|
x
|
|
|
|
dbBLOBSIZE |
|
|
x
|
|
|
|
dbMAXPROPS |
|
|
x
|
|
x
|
x
|
dbDATABASENAME |
|
|
x
|
|
x
|
x
|
dbDATABASETYPE |
|
|
x
|
|
x
|
x
|
dbASYNCSUPPORT |
|
|
x
|
|
|
|
dbPROCEDURES |
|
|
x
|
|
|
|
dbDEFAULTTXNISO |
|
|
x
|
|
|
|
dbNATIVEHNDL |
|
|
x
|
|
|
|
dbNATIVEPASSTHRUHNDL |
|
|
|
x
|
|
|
dbUSESCHEMAFILE |
|
|
x
|
|
|
|
dbSERVERVERSION |
|
|
x
|
|
x
|
|
dbTRACEMODE |
|
|
|
|
|
|
drvMAXPROPS |
|
|
x
|
x
|
x
|
|
drvDRIVERTYPE |
|
|
|
x
|
x
|
|
drvDRIVERVERSION |
|
|
|
x
|
x
|
|
cfgREFRESH |
x
|
x
|
|
|
|
|
curGETEXTENDEDINFO |
|
|
|
|
x
|
|
curMAXPROPS |
|
|
|
|
x
|
|
curMAXROWS |
|
|
x
|
x
|
x
|
|
curTABLENAME |
|
|
|
|
x
|
|
curTABLETYPE |
|
|
|
|
x
|
|
curTABLELEVEL |
|
|
|
|
x
|
|
curFILENAME |
|
|
|
|
x
|
|
curXLTMODE |
|
|
|
|
x
|
|
curSEQREADON |
|
|
|
|
x
|
|
curONEPASSON |
|
|
|
|
x
|
|
curUPDATETS |
|
|
|
|
x
|
|
curSOFTDELETEON |
|
|
|
|
x
|
|
curLANGDRVNAME |
|
|
|
|
x
|
|
curPDXMAXPROPS |
|
|
|
|
x
|
|
curDBMAXPROPS |
|
|
|
|
x
|
|
curINEXACTON |
|
|
|
|
x
|
|
curNATIVEHNDL |
|
|
|
|
x
|
|
curUPDLOCKMODE |
|
|
|
|
x
|
|
stmtMAXPROPS |
|
|
|
|
|
x
|
stmtPARAMCOUNT |
|
|
|
|
|
x
|
stmtUNIDIRECTIONAL |
|
|
|
|
|
x
|
stmtANSTYPE |
|
|
|
|
|
x
|
stmtLIVENESS |
|
|
|
|
|
x
|
stmtQRYMODE |
|
|
|
|
|
x
|
stmtBLANKS |
|
|
|
|
|
x
|
stmtDATEFORMAT |
|
|
|
|
|
x
|
stmtNUMBERFORMAT |
|
|
|
|
|
x
|
stmtAUXTBLS |
|
|
|
|
|
x
|
stmtTBLVECTOR |
|
|
|
|
|
x
|
stmtALLPROPS |
|
|
|
|
|
x
|
stmtALLPROPSSIZE |
|
|
|
|
|
x
|
stmtANSNAME |
|
|
|
|
|
x
|
stmtNATIVEHNDL |
|
|
|
|
|
x
|
stmtCURSORNAME |
|
|
|
|
|
x
|
stmtROWCOUNT |
|
|
|
|
|
x
|
stmtCONSTRAINED |
|
|
|
|
|
x
|
stmtFIELDDESCS |
|
|
|
|
|
x
|
stmtCURPROPS |
|
|
|
|
|
x
|
Získávací schéma a systémové informace
Některé funkce BDE vracejí schéma nebo systémové informace.
Některé funkce ve formátu DbiOpenxxxList mohou být použity pro návrat
kurzoru paměťové tabulky, jejíž záznamy obsahují požadované informace.
Ostatní funkce ve formátu DbiGetxxxDescs vracejí informace přímo
ve strukturách popisů a polích podporovaných aplikací.
-
Funkce DbiOpenList - vracejí madlo kurzoru k tabulce
v paměti obsahujících požadované informace.
-
Funkce DbiGetDescs - vracejí popisující informace
v předané struktuře.
Funkce BdiOpenList
Tato funkční volání vracejí madlo kurzoru na tabulku v paměti
s požadovanými informacemi. Tento kurzor je určen pouze pro čtení a aplikace
tedy nemůže modifikovat požadované informace. Z této tabulky informace
mohou být získávány normálním způsobem. Každý záznam může být čten z předdefinované
struktury přiřazené k funkci. Tyto struktury jsou uvedeny v souboru IDAPI.H.
Seznam funkcí |
Struktura virtuální tabulky vracených informací |
DbiOpenDatabaseList |
DBDesc |
DbiOpenDriverList |
Virtuální tabulka obsahuje pouze jednu položku CHAR |
DbiOpenFamilyList |
FMLDesc |
DbiOpenFieldList |
FLDDesc |
DbiOpenFieldTypesList |
FLDType |
DbiOpenFileList |
FILEDesc |
DbiOpenFunctionArgList |
DBIFUNCArgDesc |
DbiOpenFunctionList |
DBIFUNCDesc |
DbiOpenIndexList |
IDXDesc |
DbiOpenIndexTypesList |
IDXType |
DbiOpenLdList |
LDDesc |
DbiOpenLockList |
LOCKDesc |
DbiOpenRintList |
RINTDesc |
DbiOpenSecurityList |
SECDesc |
DbiOpenTableList |
TBLBaseDesc, TBLExtDesc, TBLFullDesc |
DbiOpenTableTypesList |
TBLType |
DbiOpenUserList |
USERDesc |
DbiOpenVchkList |
VCHKDesc |
Následující příklad ukazuje použití statické struktury
jako vyrovnávací paměti záznamů:
DBIResult rslt;
hDBICur hListCur;
IDXDesc idxDesc;
// Otevřené schéma tabulky obsahující jeden
záznam pro každý index
// současně dostupný v dané tabulce.
rslt = DbiOpenIndexList(hDb, "Sample", szPARADOX,
&hListCur);
if (rslt == DBIERR_NONE)
{
// Použití cyklu k získání všech deskriptorů
indexů
while (DbiGetNextRecord(hListCur,
dbiNOLOCK,
(pBYTE) &idxDesc, NULL) == DBIERR_NONE)
{
...
}
// Uzavření seznamu indexů
DbiCloseCursor(&hListCur);
}
Funkce DbiGetDescs
Tyto funkce jsou volány pro získání popisujících informací
v předané struktuře.
Seznam funkcí |
Struktura virtuální tabulky vracených informací |
DbiGetDatabaseDesc |
Struktura DBDesc |
DbiGetDriverDesc |
Struktura DRVType |
DbiGetFieldDescs |
Pole struktur FLDDesc |
DbiGetFieldTypeDesc |
Struktura FLDType |
DbiGetIndexDesc |
Struktura IDXDesc |
DbiGetIndexDescs |
Pole struktur IDXDesc |
DbiGetIndexTypeDesc |
IDXType |
DbiGetTableTypeDesc |
Struktura TBLType |
DbiQGetBaseDescs |
Struktura STMTBaseDesc |
Následující příklad ukazuje jak získat všechny deskriptory
indexů v jednom funkčním volání.
DBIResult rslt;
hDBICur hCursor;
CURProps curProps;
pIDXDesc pIdxArray;
// Otevření tabulky
rslt = DbiOpenTable(hDb, "Sample", szPARADOX,
NULL, NULL, 0,
dbiREADWRITE, dbiOPENSHARED,
xltFIELD,TRUE, NULL, &hCursor);
if (rslt == DBIERR_NONE)
{
// Získání vlastností pro kurzor
DbiGetCursorProps(hCursor, &curProps);
// Alokování vyrovnávací paměti pro
deskriptory indexů
pIdxArray = (pIDXDesc) malloc(sizeof(IDXDesc)*curProps.iIndexes);
// Získání indexů
rslt = DbiGetIndexDescs(hCursor, pIdxArray);
if (rslt == DBIERR_NONE)
{
...
}
// Úklid
free((pCHAR) pIdxArray);
DbiCloseCursor(&hCursor);
}
Vytváření tabulek
Aplikace může vytvářet trvalé tabulky pomocí funkce BDE DbiCreateTable.
Může také vytvářet dočasné tabulky voláním DbiCreateTempTable a
tabulky v paměti voláním DbiCreateMemTable.
Trvalé tabulky jsou pojmenované a jsou uložené na disk.
K vytvoření trvalé tabulky, aplikace nejprve vytvoří pro každou položku
strukturu deskriptoru položky FLDDesc a pro každý index strukturu
deskriptoru indexu IDXDesc. Pro tabulky SQL a Paradoxu, aplikace
může také definovat strukturu deskriptoru pro každé testování přípustnosti
VCHKDesc,
strukturu deskriptoru pro každé testování referenční integrity
RINTDesc
a každé testování bezpečnosti SECDesc.
Dále aplikace vytváří strukturu deskriptoru tabulky CRTblDesc
definující obecné atributy tabulky a přebírající ukazatele na pole struktur
deskriptorů položek, indexů, přípustností, referenční integrity a bezpečnosti
vytvořené dříve. Nakonec aplikace volá DbiCreateTable a předává
ji strukturu CRTblDesc.
Když vytváříme tabulku Paradoxu, dBASE, FoxPro nebo Access,
pak v posledních třech položkách struktury CRTblDesc mohou být obsaženy
volitelné parametry závisejícím na ovladači. K získání seznamu a popisu
těchto volitelných parametrů pro ovladač, aplikace volá DbiOpenCfgInfoList,
předáním cesty vytvářecích voleb ovladače tabulky v konfiguračním souboru.
Tato funkce vrací paměťovou tabulku s požadovanými informacemi.
Dočasné tabulky jsou zrušeny při uzavření kurzoru. Aplikace
může vytvářet dočasné tabulky stejně jako vytváří trvalé tabulky, pouze
místo volání
DbiCreateTable je použito DbiCreateTempTable.
Paměťové tabulky nemohou být ukládány jako trvalé tabulky.
Aplikace může vytvořit paměťovou tabulku voláním DbiCreateMemTable
a předáním pole struktur položkových deskriptorů FLDDesc. Jsou podporovány
pouze logické typy BDE.
Omezení integrity
Když vytváříme tabulku pomocí funkce BDE DbiCreateTable,
pak můžeme použít omezení integrity k zajištění aby odkazy v položkách
klíče v sekundární tabulce (ve stejné databázi) nebo cizích tabulek (v
různých databázích) byly udržovány položkami klíče v primární tabulce.
Např. pokud několik tabulek se odkazuje na primární klíč jiné tabulky,
pak musí být zajištěno, aby záznamy na které se odkazujeme nemohly být
zrušeny.
Omezení integrity primárních a cizích klíčů nejsou implementována
ve všech SQL serverech.
Podpora primárního klíče:
-
Určíme, které položky nebo množiny položek budou pracovat
jako primární klíč pro vytvářenou tabulku. Pro tabulku dBASE, zvolíme index,
který bude pracovat jako primární klíč vytvářené tabulky.
-
Vložímu tuto informaci do struktury IDXDesc.
-
Nastavíme IDXDesc.bPrimary = TRUE;
-
Připojíme strukturu IDXDesc k ukazateli CRTBLDesc.pidxDesc.
Sloupce primárního klíče musí být NOT NULL, což znamená že
VCHKDDesc
pro každý sloupec musí být VCHKDesc.bRequired = TRUE. Výjimkou je
Paradox, který může mít jeden prázdný záznam.
Tabulka může mít pouze jeden primární klíč.
Tabulka s omezením primárního klíče (tabulková úroveň)
je vytvořena a je také vytvořen unikátní index (vzestupný) na těchto sloupcích.
Pro dBASE může být použit i sestupný index. Pro vzdálené databáze, tento
index může být přidán nebo zrušen pomocí CREATE INDEX nebo DROP INDEX.
Podpora cizího klíče:
-
Určíme které tabulky (jiné tabulky) se budou odkazovat na
vytvářenou tabulku. Pokud to podporuje server, pak se může jednat i o stejnou
tabulku.
-
Určíme, které sloupce této tabulky ukazují na sloupce jiných
tabulek.
-
Určíme, co bude akce referenční integrity pro rušení. Pokud
je požadováno kaskádovité rušení, pak nastavíme RINTDesc.eDelOp = rintCASCADE.
Je-li to podporováno.
-
Vložíme tyto informace do struktury RINTDesc.
-
Připojíme RINTDesc k ukazaleli CRTBLDesc.printDesc.
Může být více než jeden cizí klíč v tabulce.
Poznámka: Některé servery, jako InterBase 4.0,
vytvářejí indexy na sloupcích cizího klíče v tabulce.
Modifikování struktury tabulky
Po vytvoření tabulky, ji aplikace může modifikovat pomocí
funkcí BDE takto:
-
Přidávat, rušit nebo regenerovat indexy.
-
Restrukturalizovat tabulku.
Aplikace může přidávat index k tabulce voláním DbiAddIndex
a předáním struktury IDXDesc zaplněnou příslušnou položkou.
Aplikace může zrušit index voláním DbiDeleteIndex.
Aplikace může tabulku specifikovat jménem nebo na tabulce otevřít kurzor.
Rušený index nemůže být aktivní.
Aplikace může regenerovat indexy dBASE, FoxPro, Access
nebo Paradoxu voláním dvou funkcí BDE. DbiRegenIndex regeneruje
jednoduché indexy, zatímco DbiRegenIndexes je zapotřebí použít k
regeneraci složitých indexů. Aplikace specifikuje index jménem.
Současně pouze pro tabulky Paradoxu, dBASE, FoxPro a
Access, aplikace může volat DbiDoRestructure k modifikaci existujících
typů položek, přidávání nových položek, rušení položky, změně pořadí položek,
změně indexů nebo referenční integrity. Aplikace předává strukturu deskriptoru
tabulky
CRTblDesc.
Používání zpětného volání
Někdy aplikace potřebuje oznámit specifický typ události
modulu přístupu k databázi pro dokončení operace nebo poskytnutí informací
uživateli. Výhodou použití zpětného volání je, že BDE může získat reakci
uživatele a to bez přerušení normálního běhu aplikace.
Ve funkcích zpětného volání musí být přísně dodrženy
tato pravidla:
-
Žádné další volání nesmí být provedeno uvnitř funkce zpětného
volání.
-
BDE je v průběhu zpětného volání nerentrantní. Aplikace nesmí
využívat Windows ve zpětném volání. Např. pokud aplikace zobrazuje dialogové
okno ve Windows, pak toto okno musí být systémově modální.
Aplikace může volit oznamování z mnoha různých typů událostí,
v závislosti na registrovaném typu zpětného volání. Aplikace může specifikovat
následující typy zpětného volání pomocí DbiRegisterCallback.
Zpětné volání |
Popis |
cbGENPROGRESS |
Informuje aplikaci o zpracovávání v průběhu velké dávkové
operace. |
cbRESTRUCTURE |
Předává informace o probíhajících akcích a vyžaduje reakci
od volajícího. |
cbBATCHRESULT |
Výsledek dávkového zpracováni. |
cbTABLECHANGED |
Oznamuje uživateli, že tabulka byla změněna. |
cbCANCELQRY |
Umožňuje uživateli zrušit dotaz Sybase. |
cbINPUTREQ |
Ovladač BDE vyžaduje vstup od uživatele. |
cbDBASELOGIN |
Umožňuje klientu přístup k zakódovaným tabulkám dBASE. |
cbFIELDRECALC |
Rekalkulace položek. |
cbDBLOGIN |
Přihlášení k databázi. |
cbDELAYEDUPD |
Zpětné volání odložených aktualizací. |
cbNBROFCBS |
Počet zpětných volání. |
cbTRACE |
Sledování. |
Deklarace funkcí zpětného volání a přiřazené seznamy parametrů,
návratové typy funkcí a typy dat zpětného volání jsou definovány v hlavičkovém
souboru IDAPI.H, který je aplikačním rozhraním k BDE.
Aplikace reaguje na zpětné volání návratovým kódem, který
je příkazem k provedení příslušné akce:
Návratový kód |
Popis akce |
cbrUSEDEF |
Proveď implicitní akci. |
cbrCONTINUE |
Pokračuj. |
cbrABORT |
Zrušení operace. |
cbrCHKINPUT |
Předání vstupu. |
cbrYES |
Provedení požadované akce. |
cbrNO |
Neprovedení požadované akce. |
cbrPARTIALASSIST |
Asistence v kompletování práce. |
Předpokládejme, že aplikace musí překopírovat tabulku
s řádově milionem záznamů a že chceme periodicky zobrazovat na obrazovce
informaci o průběhu kopírováni. Můžeme postupovat takto:
-
Zapíšeme tělo zpětně volané funkce, deklarované s přiřazeným
předdefinovaným seznamem parametrů:
typedef CBRType far *pCBRType;
typedef CBRType (DBIFN * pfDBICallBack)
(
CBType
ecbType, // Typ zpětného volání
UINT32
iClientData, // Klientská data zpětného volání
pVOID
pCbInfo // Zpětné volání informující
klienta
Input
);
-
Aplikace alokuje paměť pro vyrovnávací paměť pCbBuf
použitou k předávání dat oběma směry mezi aplikací a funkcí a ukazatel
na strukturu CBPROGRESSDesc.
typedef struct
{
INT16
iPercentDone; // Procento dokončení
DBIMSG
szMsg;
// Zpráva k zobrazení
} CBPROGRESSDesc;
typedef CBPROGRESSDesc far * pCBPROGRESSDesc;
-
K registraci zpětného volání aplikace volá DbiRegisterCallback
s předáním cbGENPROGRESS jako hodnotu pro ecbType.
-
Aplikace zajistí volání DbiBatchMove.
-
BDE vrací procento dokončení (v parametru iPercentDone
struktury
CBPROGRESSDesc), nebo řetězec zprávy k zobrazení na stavovém
řádku. Aplikace může předpokládat, že pokud hodnota iPercentDone
je záporná, pak řetězec zprávy je přípustný; jinak aplikace může použít
hodnotu iPercentDone. Formát řetězce zprávy je <Text String><:><Value>,
což usnadňuje internacionalizaci aplikace. Např. Records copied: 250
-
K pokračování aplikace vrací kód cbrUSEDEF. Aplikace
může zrušit zpracování návratem hodnoty cbrABORT.
Nezávislé datové zdroje
Můžeme použit tyto techniky k dosažení nezávislých datových
zdrojů:
-
Kvalifikovat jména tabulek pomocí přezdívek definovaných
v konfiguračním souboru (nebo předávání plně kvalifikovaných cest jmen).
-
Používat pouze logické datové typy BDE.
-
Použit obecnou podmnožinu SQL podporovanou sdíleným modulem
dotazu.
Aplikace může určovat, které přezdívky jsou dostupné voláním
BDE funkce
DbiOpenDatabaseList. Tato funkce uvádí seznam všech přezdívek
databáze z konfiguračního souboru.
Filtrování záznamů
Tato sekce je úvodem k vytváření výrazového stromu použitého
v DbiAddFilter. Výrazový strom budeme potřebovat zapisovat pouze
tehdy, když chceme efektivně generovat vysoce omezený pohled na data v
tabulce, kvalifikované více neindexovanými položkami.
Filtr je mechanismus, kvalifikující data testováním všech
záznamů. Jako základní příklad předpokládejme, že chceme otevřít tabulku
Customer,
ale zobrazit pouze ty zákazníky, kteří žijí v Kalifornii. Pro použití filtru
je důležité to, že můžeme zapsat aplikaci definující filtr pro otevřený
kurzor na tabulce Customer, kde customer.state= CA. Když
filtr je aktivován, pak BDE získává pouze ty záznamy, které splňují podmínku
a tak naše aplikace může vidět a zpracovávat pouze tyto záznamy. Např.
když naše aplikace volá DbiGetNextRecord, pak všechny záznamy, kde
zákazník nežije v Kalifornii jsou přeskočeny.
K definování filtru, aplikace volá DbiAddFilter
a předává madlo kurzoru a specifikující podmínku filtru. Funkce vrací madlo
filtru aplikaci. Parametr DbiAddFilter pcanExpr ukazuje na výrazový
strom typu pBYTE. Aplikace může použít výrazový strom ke specifikaci
filtrovací podmínky.
Výhodou používání výrazového stromu k definování podmínky
filtru je to, že BDE jej může použít k optimalizaci operací filtru. Úroveň
optimalizace závisí na úrovni podpory ovladače pro rozklad výrazového stromu.
Po definování filtru, filtr musí být aktivován voláním
DbiActivateFilter.
Použití výrazového stromu
Výrazový strom je výraz filtru typu pBYTE přetypovaný
na pCANExpr. Je to třídílný blok paměti složený z:
-
Hlavičky (Header) - struktura CANExpr definuje
velikost, počet uzlů a ofset.
-
Oblasti uzlů (Node Area) - Řada podmíněných větví
(operátor a operand) ve stromě, definující strom podmínky filtru. Uzly
operandu ukazují umístění ofsetu jmen položek nebo konstant uložených v
oblasti literálů.
-
Oblast literálů (Literal Pool Area) - Použitá k uložení
jmen položek ukazujících na každý položkový uzel a konstantní hodnoty ukazující
na každý konstantní uzel.
Povšimněte si, že hlavička obsahující strukturu CANExpr
je 10 slabik dlouhá, a tedy Oblast uzlů začíná s ofsetem 10:
První uzel v Oblasti uzlů je canBinary specifikující
operand:
canExpr.iFirstNode = 10 (kde 10 je ofset
pro celý výrazový strom)
canExpr.iLiteralStart = 48 (kde 48 je ofset
pro celý výrazový strom)
canBinary.Operand1 = 12 (kde 12 je ofset
v Oblasti uzlů)
canBinary.Operand2 = 24 (kde 24 je ofset
v Oblasti uzlů)
canField.iNameOffset = 0 (kde 0 je ofset
v Oblasti literálů)
canConst.iOffset = strlen( <fieldName>
)+1 (kde hodnota konstanty je právě za jménem
položky v Oblasti literálů)
Příklad:
Normálně používáme strom výrazů k získání pohledu pomocí
podmínky stromu, která může být značně složitá. Pro srozumitelnost, v tomto
příkladě, použijeme jednoduchý filtr, k zobrazení pouze těch záznamů kde
"CUST_NO>1500". Naší úlohou je vytvoření stromu výrazu CUST_NO > 1500.00
k předání DbiAddFilter. Následující graf tento výrazový strom popisuje:
Stejný výrazový strom je definován v C jako parametr
k předání funkci
DbiAddFilter. Následující příklad předpokládá,
že překladač alokuje souvisle deklarované proměnné ve fyzické souvislé
paměti:
void Filter (void)
{
hDBIDb
hDb = 0;
// Madlo databáze.
hDBICur
hCur = 0; //
Madlo tabulky.
DBIResult
rslt;
// Hodnota vrácená z funkcí IDAPI.
pBYTE
pcanExpr; // Struktura obsahující informace filtru.
hDBIFilter
hFilter;
// Madlo filtru.
UINT16
uSizeNodes; // Velikost
uzlu ve stromu.
UINT16
uSizeCanExpr; // Velikost informací
hlavičky.
UINT32
uSizeLiterals; // Velikost literálů.
UINT32
uTotalSize; // Celková
velikost výrazu filtru.
UINT32
uNumRecs = 10; // Počet záznamů k zobrazení.
CANExpr
canExp;
// Obsah informací hlavičky.
struct {
CANBinary BinaryNode;
CANField FieldNode;
CANConst ConstantNode;
}
Nodes = {
// Uzly stromu filtru.
{
// Posunutí 0
nodeBINARY,
// canBinary.nodeClass
canGT,
// canBinary.canOp
sizeof(Nodes.BinaryNode), // canBinary.iOperand1
sizeof(Nodes.BinaryNode) + sizeof(Nodes.FieldNode),
// canBinary.iOperand2
// Posunutí v poli Nodes
},
{
// Posunutí sizeof(Nodes.BinaryNode)
nodeFIELD,
// canField.nodeClass
canFIELD,
// canField.canOp
1,
// canField.iFieldNum
0,
// canField.iNameOffset: szField je
// literál s posunutím 0
},
{
// Posunutí sizeof(Nodes.BinaryNode) + sizeof(Nodes.FieldNode)
nodeCONST,
// canConst.nodeClass
canCONST,
// canConst.canOp
fldFLOAT,
// canConst.iType
sizeof(fConst),
// canConst.iSize
8,
// canConst.iOffset: fconst je
// literal s posunutím strlen(szField) + 1
}};
static const char szTblName[] = "cust";
// Jméno tabulky
static const char szTblType[] = szDBASE;
// Typ tabulky
static const char szField[] =
"CUST_NO"; // Jméno položky pro třetí uzel
static const DFLOAT fConst
= 1500.0; // Hodnota konstanty pro druhý uzel.
Hlavička výrazového stromu definuje:
-
Značku verze výrazu
-
Velikost struktury stromu
-
Počet uzlů v oblasti uzlů
-
Posunutí umístění prvního uzlu a začátku oblasti literárů
Hlavička má tento tvar:
#define CANEXPRVERSION 2
typedef struct{
UINT16 iVer;
UINIT16 iTotalSize;
UINT16 iNodes;
UINT16 iNodeStart;
UINT16 iLiteralStart;
} CANExpr;
typedef CANExpr far *pCANExpr;
typedef pCANExpr far *ppCANExpr;
Oblast uzlů výrazového stromu
Každý uzel formuje větev stromu a definuje podmínku.
Uzly mohou definovat operátory nebo operandy. Operand uzlu ukládá posunutí
jména položky nebo konstanty v oblasti literárů. Hodnoty jsou uložené v
oblasti literálů. Položkový uzel ukazuje na posunutí umístění jména položky
obsahující literál, tj. znakový řetězec jména položky, který musí být ukončen
znakem s kódem nula. Uzel konstanty ukazuje na hodnotu konstanty v oblasti
literálů.
Uzly operátorů jsou různých typů:
Relační uzly operátorů
Výčtový typ |
Popis |
canISBLANK |
Unární, prázdný operand |
canNOTBLANK |
Unární, neprázdný operand |
canEQ |
Binární, rovno |
canNE |
Binární, nerovno |
canGT |
Binární, větší než |
canLT |
Binární, menší než |
canGE |
Binární, větší nebo rovno |
canLE |
Binární, menší nebo rovno |
Logické uzly operátorů
Výčtový typ |
Popis |
canNOT |
Unární, NOT |
canAND |
Binární, AND |
canOR |
Binární OR |
Aritmetické
uzly operátorů
Výčtový typ |
Popis |
canMINUS |
Unární, minus. Není podporováno všemi ovladači SQL. |
canADD |
Binární, součet. Není podporováno všemi ovladači SQL. |
canSUB |
Binární, rozdíl. Není podporováno všemi ovladači SQL. |
canMUL |
Binární, násobení. Není podporováno všemi ovladači SQL. |
canDIV |
Binární, dělení. Není podporováno všemi ovladači SQL. |
canMOD |
Binární, celočíselné dělení. Není podporováno všemi ovladači
SQL. |
canREM |
Binární, zbytek po dělení. Není podporováno všemi ovladači
SQL. |
Různé uzly operátorů
Výčtový typ |
Popis |
canCONTINUE |
Unární; zastavuje vyhodnocování výrazu, když je operand
vyhodnocen jako false (umožňuje zastavit na horní hranici
hodnoty filtru). |
Uzly operátorů ukazují na posunutí jejich uzlů operandů.
Oblast literálů
Oblast literálů je použit k ukládání jmen položek použitých
jednotlivými uzly položek a konstantní hodnoty použité jednotlivými uzly
konstant. Jméno položky je tvořeno literálem. Hodnoty konstant musí být
reprezentovány pouze logickými typy BDE.
Např. následující logická podmínka je reprezentována
jako parametr výrazového stromu:
CUST_NO <= 1500 AND CUST_NO >= 1300
Následující příklad předpokládá, že překladač alokuje
souvisle deklarované proměnné ve fyzické souvislé paměti
static const char szTblName[] = "cust";
// Jméno tabulky
static const char szTblType[] = szDBASE;
// Typ tabulky
static const char szField[] =
"CUST_NO"; // Jméno pro první uzel položky
static const char szField2[] = "CUST_NO";
// Jméno pro druhý uzel položky
static const DFLOAT fConst
= 1500.0; // Hodnota prvního uzlu konstanty
static const DFLOAT fConst2 =
1300.0; // Hodnota druhého uzlu konstanty
void Filter (void)
{
hDBIDb
hDb = 0;
// Madlo databáze.
hDBICur
hCur = 0; //
Madlo tabulky.
DBIResult
rslt;
// Vrácená hodnota funkcemi IDAPI.
pBYTE
pcanExpr; // Struktura obsahující informace filtru.
hDBIFilter
hFilter;
// Madlo filtru.
UINT16
uSizeNodes; // Velikost
uzlů ve stromu.
UINT16
uSizeCanExpr; // Velikost informací
hlavičky.
UINT32
uSizeLiterals; // Velikost literálů.
UINT32
uTotalSize; // Celková
velikost výrazu filtru.
UINT32
uNumRecs = 10; // Počet záznamů k zobrazení.
CANExpr
canExp;
// Obsah informací hlavičky.
struct {
CANBinary MainNode;
CANBinary BinaryNode1;
CANField FieldNode1;
CANConst ConstantNode1;
CANBinary BinaryNode2;
CANField FieldNode2;
CANConst ConstantNode2;
}
Nodes = {
// Uzly stromu filtru.
{
// Posunutí 0
nodeBINARY,
// canBinary.nodeClass
canAND,
// canBinary.canOp
sizeof(Nodes.MainNode),
// canBinary.iOperand1
sizeof(Nodes.MainNode)
+ sizeof(Nodes.BinaryNode1)
+ sizeof(Nodes.FieldNode1)
+ sizeof(Nodes.ConstantNode1),// canBinary.iOperand2
// Posunutí v poli Nodes
},
{
// Posunutí sizeof(Nodes.MainNode)
nodeBINARY,
// canBinary.nodeClass
canLE,
// canBinary.canOp
sizeof(Nodes.MainNode)
+ sizeof(Nodes.BinaryNode1), // canBinary.iOperand1
sizeof(Nodes.MainNode)
+ sizeof(Nodes.BinaryNode1)
+ sizeof(Nodes.FieldNode1), // canBinary.iOperand2
// Posunutí v poli Nodes
},
{
// Posunutí sizeof(Nodes.MainNode) + sizeof(Nodes.BinaryNode1)
nodeFIELD,
// canField.nodeClass
canFIELD,
// canField.canOp
1,
// canField.iFieldNum
0 ,
// canField.iNameOffset: szField je
// literal s posunutím 0 (začátek oblasti literálů)
},
{
// Posunutí sizeof(Nodes.MainNode) + sizeof(Nodes.BinaryNode1)
// + sizeof(Nodes.FieldNode1)
nodeCONST,
// canConst.nodeClass
canCONST,
// canConst.canOp
fldFLOAT,
// canConst.iType
sizeof(fConst),
// canConst.iSize
sizeof(szField),
// canConst.iOffset: fConst je
// literal s posunutím sizeof(szField)
},
{
// posunutí sizeof(Nodes.MainNode) + sizeof(Nodes.BinaryNode1)
// + sizeof(Nodes.FieldNode1) + sizeof(Nodes.ConstantNode1)
nodeBINARY,
// canBinary.nodeClass
canGE,
// canBinary.canOp
sizeof(Nodes.MainNode)
+ sizeof(Nodes.BinaryNode1)
+ sizeof(Nodes.FieldNode1)
+ sizeof(Nodes.ConstantNode1)
+ sizeof(Nodes.BinaryNode2), // canBinary.iOperand1
sizeof(Nodes.MainNode)
+ sizeof(Nodes.BinaryNode1)
+ sizeof(Nodes.FieldNode1)
+ sizeof(Nodes.ConstantNode1)
+ sizeof(Nodes.BinaryNode2)
+ sizeof(Nodes.FieldNode2), // canBinary.iOperand2
// Posunití v poli Nodes
},
{
// Posunutí sizeof(Nodes.MainNode) + sizeof(Nodes.BinaryNode1)
// + sizeof(Nodes.FieldNode1) + sizeof(Nodes.ConstantNode1)
// + sizeof(Nodes.BinaryNode2)
nodeFIELD,
// canField.nodeClass
canFIELD,
// canField.canOp
2,
// canField.iFieldNum
sizeof(szField)+sizeof(fConst), // canField.iNameOffset: szField2 je
// literal s posunutím sizeof(fConst)
// + velikost první položky
},
{
// Posunutí sizeof(Nodes.MainNode) + sizeof(Nodes.BinaryNode1)
// + sizeof(Nodes.FieldNode1) + sizeof(Nodes.FieldNode1)
// + sizeof(Nodes.BinaryNode2) + sizeof(Nodes.FieldNode2)
nodeCONST,
// canConst.nodeClass
canCONST,
// canConst.canOp
fldFLOAT,
// canConst.iType
sizeof(fConst2),
// canConst.iSize
sizeof(szField)
+ sizeof(fConst)
+ sizeof(szField2),
// canConst.iOffset: fconst je
// literal s posunutím sizeof(fConst)+ velikost
// první položku + druhé položky
}};
Následující graf reprezentuje stejný výraz (posunutí
jsou uvedena v závorkách).
Hlavička:
- - - - - - - - - - - - - - - - - - - - - - - - -
Binární uzel:
AND (0)
Binární uzly:
LE (12)
GE (50)
Uzly konstant a položek:
FIELD (24) CONST (36) FIELD (62) CONST (74)
Oblast literálů:
CUST_NO (0) 1500 (8) CUST_NO (16) 1300 (24)