You are on page 1of 20

Delphi + ZeosLib + Firebird

Instalacin, comentarios y ejemplos de uso


A ZEOS basics tutorial not only for Firebird ...

Author Michael Seeger (ZeosLib Development Team)


Date 26.03.2008, 16:01 Views 6143 at 22,03,2010
Description A ZEOS basics tutorial not only for Firebird ...
Category Firebird / Interbase Type

The ZeosLib DBOs 6.1.5 - With Delphi 7 and Firebird 1.5 This little article shows how
to access Firebird databases by using the ZEOS component Library in version
6.1.5 (including Patches 1&2) and how to use these components in database
applications. It does not matter if you use the "real" SQL-Server or the
embedded version which is restricted to local held databases. A couple of
examples (also migrated Delphi-BDE demos) shall explain how to use the ZEOS
components.

Although this article describes the usage of the ZEOS Library using Firebird,
all the basics can be used with other SQL servers / databases that are supported
by ZEOS.

Note: The Firebird Server can be downloaded from download section of


http://www.ibphoenix.com
http://www.firebirdsql.org

The ZEOS Library


The name "ZEOS" has no special meaning. The founders of ZEOS found that this
name just sounded good. Since that time the Library is called "ZEOS".

Generally we can say about the ZEOS Library that the developers are inteneded to
copy the functions and the behaviour of the corresponding BDE components as good
as possible. The intension is to minimize the learning courve for developers who
migrate from BDE to ZEOS. Of course there must have been made some compromises
so that they are not a hundred percent compatible because the ZEOS components
shall be applicable universally.

The ZEOS Library in version 6.1.5 consists of the following nine components
which shall be introduced in the following:
TZConnection
TZQuery
TZReadOnlyQuery
TZUpdateSQL
TZTable
TZStoredProc
TZSQLProcessor
TZSQLMonitor
TZSQLMetadata
InstallingtheZEOSLibraryandadditionalstuff
TheinstallationoftheZEOSLibraryunderDelphi7professionalisnotthatcomplicated.Oncethecurrent
ZEOSversionandallthepatches(whilewritingthisarticleitwaslibraryversion6.1.5andthecorresponding
patches1&2)aredownloadedandunzippedintoadirectoryofyourchoicecompletelyyouonlyhaveto
followtheinstallationinstructions(note:FirebirddoesnotneedanyadditionalDLL's,here!):

OpenthedelphiprojectgroupZeosDbo.bpgfromsubdirectorypackages\delphi7ZeosDbo.bpgandinstall
thefollowingcomponentsingivenorder:
ZCore.bpl
ZParseSql.bpl
ZPlain.bpl
ZDbc.bpl
ZComponent.bpl

Note:Ifthereoccursomeerrorswhilecompilingthatsaythatacertaindcufilecouldnotbefoundthenjust
addthesubdirectorypackages\delphi7\buildtodelphislibrarypath.Alldcufilesthatarecreatedwhile
compilationarelocatedhere.

Attention:TheclientlibraryofFirebirdServerversion1.5.1(notembedded!)wasdeliveredas"gds32.dll"
andnot"fbclient.dll".ThiscausestroublewhileaccessingviaZEOSbecausetheprotocol"firebird1.5"
aassumesaDLLnamed"fbclient.dll".Aworkaroundistocopythe"gds32.dll"andrenamethiscopyto
"fbclient.dll".

Additive:EventAlertercomponent(TZIBEventAlerter)

Anadditionalcomponent,thatonlyexistsforFirebird(andalsoInterbase)interceptsall(registered)events,
thataretriggeredbyStoredProceduresofadatabase,andmakesitpossibletoreactuponthem.This
componentwaspostedbyAlexeyGilev(aka"GAF")intotheZEOSForumandmadeavailabletotheZEOS
Team.Theoriginalversioncanbedownloadedhere:
http://sourceforge.net/tracker/index.php?func=detail&aid=911233&group_id=35994&atid=415826

TheeventalerterisonlytestedforZEOSversion6.1.4butrunswithsomemodificationswithoutany
problemsinversion6.1.5(withPatch1&2).TheeventalerterthatwasportedtoZEOSversion6.1.5willbe
installedasfollows(pleaseconsidertheReadMefile!I'mnotgoingtogiveanywarrentythatthissolutionis
errorfree!Youuseitatyourownrisk!):
DownloadpatchforZIBEventAlerter(integrated)here
unzipthefilesanddistributethemtotheaccordingZEOSdirectories
ifnecessaryrecompileandreinstallZEOS(seeabove)

Basics:Transactions
SomemandatorybasicsabouttransactionshavetobetoldinordertounderstandhowtheTZConnection
componentworksinternallyandhowtoworkwithit.

Ingeneral:Youonlycanhaveaccesstoadatabasewithinthecontextofavalid("running")transaction.A
transactionhastofulfillthefollowingfourcharacteristicsthatareknownasACIDcharacteristicsofa
transaction.
Atomicity
Allactionsperformedonadatabasehavetobeexecutedsuccessfully.Ifonlyoneerroroccursthenthe
originalstateofthedatabasehastoberestored.Accordingtotheprinciple"allornothing".

Consitency
Atransaktiontransfersadatabasefromoneconsitentstatetoanotherconsistentstate.Ifanerroroccurs
thentheoriginalstateofthedatabasehastoberestored.

Isolation
Atransactionhastobehandledbytheserverasifitwastheonlyonerunning.Thismeansithastorun
indepentlyfromothertransactions.Ausermustnotnoticethechangesdonebyotherusers.

Durability
ChangesinthedatasetofadatabasethatarecausedbySQLstatementsthatareexecutedbetweenthe
startofatransactionandaCOMMIThavetobefixedirrevocably.

Transactionsencapsulateconsecutiveaccessesonadatabase.Adatabaseaccessmaybeofreadingor
writingnature(INSERT,UPDATE,DELETE)oritmaychangethestructureofadatabase.Transactionsare
terminatedbyCOMMITorROLLBACK.COMMITconfirmsallchangesinadatabasesincethestartofa
transaction.ROLLBACKresetsallchangesinadatabasesincethestartofatransaction.

Transactionsrunningontheserverhavetobeisolatedfromeachother.Sotheymayrunindependently.A
transactionhastobehandledbytheserverasitwastheonlyonethatiscurrentlyrunning.Thismeansfor
theuserthathemustneverseethechangesofotheruserswhileheisinarunningtransactionbecausethe
otherchangeshavenothingtodowithhistransaction.Thisiscalledtheisolationofatransaction.Byusing
differenttransactionisolationlevels(TILs)thedevelopermayprotectthedataofanSQLresultsetfrom
accessbyothertransactions.Thebehaviourdescribedaboveiscalledthestandardisolationlevelknownas
SERIALIZABLE.ThestandardisolationlevelofFirebirdiscalledSNAPSHOTandcomesverycloseto
SERIALIZABLE.

TZConnection
TheTZConnectioncomponentisacombinationofaBDETDatabaselikecomponentacomponentthat
handlesatransaction.ThiscombinationmakessensebecauseallaccesstoaFirebirddatabase(andalso
otherdatabases)isalwaysmadeinarunningtransaction.SuchatransactionisstartetbytheZEOSLibrary
wheneveraconnection(methodConnectofTZConnection)toadatabaseisopened.Thiscausesthateach
databaseaccessisdonewithinthecontextofarunningtransaction,automatically.Thesocalled
AutoCommitmodeisalwayson(setto"True").ThisisalsothestandardbehaviourofthecorrespondingBDE
component.IfAutoCommitisactivatedtheneverychangeofanSQLstatementwillbeconfirmedinthe
databasebyCOMMITateritssuccessfullexecution.Ifthisbehaviourshallbeturnedoffandanexplicit
transactionshallbestartedthenthemethodStartTransactionhastobecalled.Withinthisexplicittransaction
itispossibletoexecuteacoupleofSQLstatementsthatmakechangestothedatabase,insuccession.
Thesestatementsthencanbeconfirmedasa"group"byCOMMIT.Ifanexplicittransactionisactivethen
AutoCommitisalwaysturnedoff.ByCallingtheMethodCommitallthechangesmadewithinthisexplicit
transactionareconfirmed.CallingthemethodRollbackresetsthesechanges.InbothcasesAutoCommitwill
besettoTruewhenthemethodcall(CommitorRollback)isdone.Theexplicittransactionhasended.

Retaining

AfterconfirmingthechanesmadeinatransactionbyCOMMITorresettingthembyROLLLBACKthe
transactionnormallyisgoingtobeendedandanexistingresultsetofaqueryorstoredprocedurewillbe
discarded.TheseCOMMITsandROLLBACKsarecalled"hard"commitor"hard"rollback.Byusingthe
ZEOSlibrarythiswillbecomealittlebitdifferent.ZEOSkeepstheresultsetalive.Thisisachievedbyclosing
transactionwith"soft"commitsor"soft"rollbacks.AllthisisdonebytheTZConnectionobject.Thismethodis
calledretaining.TheCOMMITandROLLBACKcommandsareexecutedwiththeadditionRETAINING.
Retainingcausestheclosingofthecurrenttransactionandimmediatelyopeninganewtransactionwithall
thedataandresources(especiallytheresultset)ofthe"old"transaction.

Retainingbecomesaproblemifitisusesforhugetables.Itconstrainstheinternalcleanupmechanismof
firebird(garbagecollection).Thisleads(becauseoftheversioningandthemultigenerationalarchitectureof
Firebird)toalotofoldrecordsthathavetobekeptbutwillnotbeneededanymore.Thisinfluencesthe
server'sperformancedinanegativeway.Asocalledsweepwoulddiscardtheseoldversionsandimprove
theperformance.Thissweepwillonlybeexecutedbysendinga"hard"COMMITorROLLBACK.TheZEOS
Libraryonlyexecutesthese"hard"commandswhenendingthedatabaseconnection(closingconnection).It
isnotpossibletosendthemwhileadatabaseconnectionisactive.Sothedatabaseconnectionshouldbe
deactivatedandimmediatelyactivatedoccasionallytoachievethisperformanceimprovement.

TransactionIsolationLevelsofTZConnection

TheTZConnectioncomponentprovidesfourusefulandpredefinedTransactionIsolationLevels(TIL):

tiRepeatableRead
ItcorrespondstotheTIL"SNAPSHOT"whichisthestandardofFirebirdservers.Itisacombinationofthe
trasactionparameters"concurrency"and"nowait".Asnapshotofthecurrentdatabaseismade.Otherusers
areonlyinfluenced(constrained)iftwotransactionsworkononerecordsimultaneously.Ifconflictsarise
whileaccessingdataanerrormessagewillbereturned.Changeswithinothertransactionswillnotbe
noticed.ThisTILcoverstherequirementoftheSQLstandard(SERIALIZABLE)widely.

tiReadCommitted
ItcorrespondstotheTIL"READCOMMITTED".Itisacombinationofthetransactionparameters
"read_committed","rec_version"and"nowait".ThisTILrecognizesallchangesinothertransactionthathave
beenconfirmedbyCOMMIT.Theparameter"rec_version"isresponsibleforthebehaviourthatthemost
currentvaluesthatwerecommittedbyotheruserswillbeconsidered.theparameter"nowait"isresposiblefor
thebehaviourthatthereisnowaitingforthereleaseofalockedrecord.Sotheserverismorestressedthan
inTILtiRepeatableReadbecauseithastodoalltherefreshestogetthesevaluesagainandagain.

tiSerializable
ItcorrespondstotheTIL"SNAPSHOTTABLESTABILITY".Itisusedtogetanexclusiveaccesstotheresult
set.Realizedbytransactionparameter"consistency"itpreventsthat"foreign"transactionmayaccessthe
writtendata.Onlythetransactionwhichhaswrittenthedatamayaccessthem.Thispreventsalsoamulti
useraccesstothewrittendata.BecausethisTILisveryrestrictivebyaccessingwrittendataitshouldbe
appliedwithcautionandcare.

tiNone
NoTILisusedtoisolatethetransaction.

TheTILtiReadUncommittedisnotsupportedbyFirebird.IfthisTILisused,anerrorwillbetriggerdedand
thetransactionwillnotbeisolated(likeusingtiNone).

Recommendation

ItisadvisabletoisolatetransactionswithtransactionisolationleveltiRepeatableRead(theFirebirdstandard).
ThisTILcoverstherequirementoftheSQLstandard(SERIALIZABLE)widely.Itpreventsallproblems
concerningconsistencythatmayarisebyusingtransactions.SecondchoicewouldbetiReadCommittedbut
thisdependsontheapplicationandthenecessityifthereseultsetalwayshastobecurrent.
CustomizingTILs

IfyouwanttocustomizeyourTILsorexpandagivenTILthenyoucandothisbyusingtheparametersof
TZConnection.ThemostimportantthingaboutthisisthattheTILthatshouldbeexpandedhastobesetin
propertyIsolationLevel.IfitissettheTILparameters(seeIB/FBAPIreference)youwanttoaddmaybe
addedinsourcecode.ThefollowingexampleshowstheexpansionofaTILpresettotiNone:
:
ZConnection.TransactIsolationLevel := tiNone;
ZConnection.Properties.Add('isc_tpb_concurrency');
ZConnection.Properties.Add('isc_tpb_wait');
ZConnection.Connect;
:

Protocol

ThemostimportantsettinginaTZConnectionobjectistheserverprotocolitissetinpropertyProtocol.It
determineswhichprotocolshallbeusedandthuswhichSQLserverwillbeaccessed.Thismethodmakes
ZEOSsoflexible.Youdon'thavetoinstallspecialcomponentsforeachdatabaseyouwanttoaccessasit
wasinVersions5.xandearlier.Thecomponentswillbeinstalledonce.Thisisenough.Youonlychoosethe
protocolforthesupportedSQLserveryouwanttoaccessandyouaredone.Soyousetprotocol"firebird
1.5"toaccessFirebird1.5server.

ReadOnlyConnection

ThedatabaseconnectionmaintainedbyaTZConnectionobjectissettoreadonlybydefault(ReadOnly=
True).Thismeansthatnowritingaccesstotheconnecteddatabaseisallowed.Togetwritingaccesstothe
databaseyouhavetosetReadOnlytoFalse.

Codepages

Codepageswillbedeterminedbysettingparameter"lc_ctype"or"Codepage"inTZConnection.This
parametermustbeaddedtothepropertyProperties.E.g.:
ZConection.Properties.Add ('lc_ctype=ISO8859_1');

or

ZConnection.Properties.Add ('Codepage=ISO8859_1');

Note:TheCodepagesupportofFirebird(alsoembeddedversion)inversion1.5withZEOSisalittlebit
buggy.ThesebugsarecorrectedinFirebirdversion1.5.1.

FeaturesoftheFirebirdembeddedserver

NormallytheservernameorIPaddressoftheserverisgiveninpropertyHostName.ByusingaFirebird
embeddedserveryoumayleavethispropertyempty.OnlypropertyDatabasehastobedetermined.Here
youhavetospecifydrivepathandnameofthedatabaseincludingextension.

Anotherfeatureoftheembeddedserveristhatyoumayspecifyanyloginnamewithapaswordofyour
choice.Itdoesn'tmatterwhatyouchooseyouwillgetconnected.

SettingtheTZConnectionobjectpropertyConnectedtoTrueindesigntimeisextremelybadifyoudon'treset
ittoFalsebeforecompiling.IfyouthenstartthecompiledapplicationwithIDErunningyouwillgetanerror
thatsaysthatthedatabasecannotbeopenedbecauseitisalreadyinuse.Soyoushouldestablishthe
connectiontothedatabasewhenstartingtheapplication(e.g.inOnCreateeventofthemainform)andthen
opentheneededqueriesandtables.Deactivatingtheconnectionshouldalsobedoneinmainform(e.g.in
OnDestroy).Itisnotnecessarytocloseallopenqueriesandtables.Thiswillbedonewhenclosingthe
connectionoftheTZConnectionobject.Ifyouusedatamodelformsyouhavetotakecarethatthedatamodel
formiscreatedbeforethemainformiscreated(setintheIDE'sprojectoptions)..

UsefulTZConnectionparameters

AdditionalparametersforestablishingconnectionstoFirebirddatabasesare::

CreateNewDataBase:
AnewdatabasewillbecreatedbasedonthespecifiedCREATEDATABASEstatements.Whenthedatabase
iscreatedtheconnectionwillbeestablishedimmediately.AllthishappensbycallingtheConnectmethodof
TZConnection.
:
ZConnection1.Database := 'd:\db1.fdb';
ZConnection1.Protocol := 'firebird-1.5';
ZConnection1.Properties.Add ('CreateNewDatabase=CREATE DATABASE ' +
QuotedStr ('d:\db1.fdb') + ' USER ' +
QuotedStr ('sysdba') + ' PASSWORD ' + QuotedStr ('masterkey') +
' PAGE_SIZE 4096 DEFAULT CHARACTER SET ISO8859_1');
ZConnection1.Connect;
:

ToexecutethiscorrectlyyouhavetosettheDatabaseandProtocolpropertiesatminimum(alsopossiblein
objectinspector).

Dialect:
ThisparametersetsFirebird'sSQLdialiect.Tosetdialect"1"youhavetousethefollowingcode:
ZConnection.Properties.Add ('Dialect=1');

ThedialectofFirebird1.5.xissetto"3"bydefault.

Rolename:
Thisparametersetsarolename.Aloggedinuserthenworksinthecontextoftherole'srightsbutbeforethe
userhastobeassignedtothisrole.TheFirebirdembeddedserverdoesnotsupportthisfeature.

TZQuery
TheusageofTZQueryissimilartotheusageofBDE'sTQuerycomponent.

Recommandation:RequestLiveandTZUpdateSQL

IfanSQLdatasetshallbeupdatablethenRequestLivehastobesettotrueandyoushouldgenerallyuse
accordingupdateSQLstatementsthatwillbedefinedinTZUpdateSQL.Ifthisisdonejustassign
TZUpdateSQLtotheTZQueryobject.Nowallchangesthatwillbemadeintheresultsetwillbedonetothe
databasebyusingthedefinedstatementsofTZUpdateSQL.AccordingtoexperienceRequestLivemode
runsmoresmoothlybyusingTZUpdateSQL.
UsageofparametersinSQLstatements

UsingparametersinSELECTstatmentsisaseasyasusingthemwithBDE'sTQuery.IfTZQueryhasa
resultsetthenyouhavetousetheOpenmethod.IfyouwanttoexecuteanSQLstatementwhichhasno
resultset(e.g.:INSERTorUPDATE)youhavetouseExecSQL(seealso:TZStoredProc).

TZReadOnlyQuery
ThisisaQuerycomponentthatisquitesimilartotheTZQuerycomponent.Thereisjustonedifference:The
resultsetisreadonly.ThereisnopossibilitytoassignaTZUpdateSQLobject.

TZUpdateSQL
ATZUpdateSQLobjectprovidesstatementstomodifythedataofaresultsetthatisretrievedbyaTZQuery
object.TheTZUpdateSQLcomponentiscomparabletoBDE'sTUpdateSQLcomponent.Hereisanexample
howtodefinethestatementsofanSQLstatementwithcorrespondingupdatestatements(basedonadialect
3database):
SQL.Sql:

SELECT * FROM names


UpdateSQL.InsertSql:

INSERT INTO names (recno, name)


VALUES (:recno, :name)
UpdateSQL.ModifySql:

UPDATE names
SET recno = :RecNo,
name = :name
WHERE recno = :old_recno
UpdateSQL.DeleteSql:

DELETE FROM names


WHERE recno = :old_recno

The"OLD_"parameterprefixforSQLstatements

The"old_"prefixishandledaccordingtothehandlingwithBDEcomponents.Byusing"OLD_"asprefixfora
fieldnameyouareabletoaccessthevalueofthefieldbeforeitwaschanged.Thisisveryhelpfulifyouhave
tocomparefieldvaluesinaWHEREclause.

Querieswithreadonlyresultsets

IngenealaTZUpdateSQLobjectisassignedtoaTZQueryobjectthathasareadonlyresultset.Thismakes
itpossibletochangeitsdata.Suchreadonlyqueriesarequeriesthatjoinmultipletables.Butalsowith
"normal""RequestLive"resultsetsyoumayuseTZUpdateSQL(see:TZQuery).
MultiplestatementsinTZQueryandTZUpdateSQL

ThecomponentsTZQueryandTZUpdateSqlprovidethepossibilitytoexecutemultiplestatements,internally.
SoitispossibletoplacemultipleSQLstatements(evenwithparameters)forexecutioninSQLproperty.
Theyonlyhavetobeseparatedbysemicolon.Hereanexample:
:
With Query do Begin
Sql.Clear;
Sql.Add('DELETE FROM table1;');
Sql.Add('INSERT INTO table1 VALUES (:Val1, :Val2);');
Sql.Add('INSERT INTO table2 VALUES (:Val3, :Val2);');
Sql.Add('UPDATE table3 SET field1 = :Val4;');
Params.ParamByName('Val1').AsInteger := 123;
:
ExecSql;
End;
:

Thestatementswillbeexecutedingivenorder.Itisalsopossibletoexecutemultiplestatementsiftheyare
groupedinthismannerinsidemultipleTZUpdateSqlObjectsinordertoupdatemultipletables.

TZTable
TZTableactslikeBDE'sTTable.AsaprincipleyouonlyshoulduseTZTableinaC/Sapplicationifyouhave
verysmalltablesbecauseallrecordsofthetablewillbetransferredfromserverintoclient'smemoryby
openingtheTZTable.Thisisabahavioursimilartoa"SELECT*FROMXYZ"statement.Youshouldeven
preventastatementlikethisinaC/Sapplication.Theintensionistokeeptheresultsetthathastobe
transferredfromservertoclientassmallaspossible(perferablyonleonerecord).

TZStoredProc
TZStoredProcprovidesthepossiblitytoexecutestoredproceduresthataresavedinadatabase.Thereare
twokindsofstoredprocedures:Proceduresthatreturnaresultsetandproceduresthatdonotreturna
resultset.TZStoredProcworkssimilartoBDE'sTStoredProc.Theonlydifferencebetweenthemisthatyou
don'thavetocallPreparebeforeyoucalltheExecProcmethod.

StoredProcedureswithResultsets

IfastoredprocedurereturnsaresultsetthenitwillbeactivatedbycallingtheOpenmethod(whenall
existingparametershavegottheirvalues):
:
With spSumByName do Begin
Close;
ParamByName ('Name').Value := 'DontKnowHow';
Open;
End;
:

TheresultsetcanbeworkedonlikearesultsetofaTZQuery.
StoredProcedureswithoutResultsets

IfastoredprocedurehasnoresultsetthenitwillbeexecutedbycallingtheExecProcmethod(whenall
existingparametershavegottheirvalues).Hereisanexample(conConnection.AutoCommit=True):
:
With spDeleteByName do Begin
ParamByName ('Name').Value := 'DontKnowHow';
conConnection.StartTransaction
Try
// execute StoredProc
ExecProc;
Except
conConnection.Rollback;
End;
conConnection.Commit;
End;
:

Attention:BuginZEOSLibraryV6.1.5!

IfastoredprocedurewasexecutedviaExecProcatleastonetime,anexceptionwillbetriggeredwhen
disconnectingTZConnection(usedbyTZStoredProc)fromdatabase.Exception'sMessageis:"invalid
statementhandle".SQLerrornumberis901.ThisExceptionwillnotbetriggeredifthestoredprocedureis
executedusingaTZQueryorTZReadOnlyQuery.Executionofthestoredprocedurewillbedonebycalling
theprocedureusingthefollowingSQLcommand(putintotheSQLproperty):
[syntax="sql"]EXECUTEPROCEDUREDeleteByName(:Name)[/color][/font][/list]
ExecutingastoredprocedurethiswayissimilartoexecutioninTZStoredProc(conConnection.AutoCommit=
True):
:
// using a TZQuery object
With qryDeleteByName do Begin
ParamByName ('Name').Value := 'DontKnowHow';
conConnection.StartTransaction
Try
ExecSQL; // execute SQL statement in TZQuery
Except
conConnection.Rollback;
End;
conConnection.Commit;
End;
:

Info:Evenwithadialect3databaseitisnotneededtoputthenameofthestoredprocedureintoquotation
marksNamesofstoredproceduresinFirebirdarenotcasesensitive.

ProblemswithTZStoredProcwhenusingdialect1databases

Theproblemwhenusingdialect1databasesisthatthemetadataofstoredproecedureisnotproperly
transferredtotheTZStoredProcobjectswhenchoosingthestoredprocedureinobjectinspector.All
parapeterdataisnotproperlyinterpreted.Anupdatefromdialect1todialect3solvesthisproblem.Isthe
dialect1databasevitalfortheapplicationsystemthenaTZQueryorTZReadOnlyQueryhastobeusedas
workaround.
TZSQLProcessor
ThiscomponentprovidesthefunctiontoprocessSQLscriptsthatcanbeloadedbycallingthemethods
LoadFromStream()orLoadFromFile().TheloadedSQLScriptisputintoanaccordingpropertycalledScript.
Importanisthatthecorrectdelimiterforthescriptisset(PropertyisalsoDelimiter).Bydefault";"willbeset
asdelimiter.Thisissufficientforthemostscripts.Butifyouwanttocreatestoredproceduresortriggersvia
scriptyoushouldsetdelimiteraccordingtothesettingofthescript's"SETTERM>newDelimiter<
>oldDelimiter<"command(normally"^"isusedforthis).InadditiontothisthepropertyDelimiterTypehasto
besettodtSetTerm.HeresomelinesofcodethatshowhowtoprocessanSQLscript:
:
sqlScript.Script.Clear;
sqlScript.LoadFromFile('c:\temp\createdb.sql');
conConnection.StartTransaction;
Try
sqlScript.Execute;
Except
conConnection.Rollback;
End;
conConnection.Commit;
:

TheSQLscriptisprocessedwithinanexplicittransaktion(AutoCommitisturnedon).Ifexecutionsucceeds
thechangeswillbecommittedotherwisetheywillberolledback.

TZSQLMonitor
UsingtheTZSQLMonitorcomponentyoumaylogcertainactionsoreventsoftheZEOSdatabase
components.ThejournalmaybewrittenasfileorcollectedinaTMemoobjectorsomethinglikethat.

Writingtheactionsoreventstoalogfileonlyneedsafewsettings:
:
sqlMonitor.FileName := 'C:\Log\MyAppLog.log';
sqlMonitor.Active := True;
sqlMonitor.AutoSave := True;
:

TocollecttheloggedactionsoreventsinaTMemoobjectyouhavetoimplementtheOnLogTraceeventas
follows:
Procedure Tfrm_MyApp.sqlMonitorLogTrace (Sender: TObject;
Event: TZLoggingEvent);
Begin
If Trim (Event.Error) > '' Then
memMontor.Lines.Add (DateTimeToStr (Event.Timestamp) + ': ' +
Event.Message + #13#10 + ' Error: ' + Event.Error)
Else
memMontor.Lines.Add (DateTimeToStr (Event.Timestamp) + ': ' +
Event.Message);
End; // sqlMonitorLogTrace

TheOnLogTraceeventisalwaystriggeredwhenanactionoreventwasloggedbysqlMonitor.LogEvent
(oEvent).TheoEventparameterstandsforaninstanceofaTZLoggingEventclassobject.
PropertiesofTZLoggingEvent

Category(TZLoggingCategory):Standsforthecategoryoftheloggedactionorevent(lcConnect,
lcDisconnect,lcTransaction,lcExecuteorlcOther)

Protocol(String):Theprotocolthatisusedtoaccessthedatabase(see:TZConnection)

Message(String):Thetextthatislogged.

ErrorCode(Integer):Theerrorcodeincaseofanerror.

Error(String):Theerrortextincaseofanerror.

Timestamp(TDateTime):Dateandtimeoftheloggedactionorevent.

TZSQLMetadata
WiththisspecialTDataSetcomponentitispossibletoaccessthemetadataofadatabaseliketables,
columns,etc.(Thischapterisstilltobeexpanded!)

TZIBEventAlerter(AddOnonlyforFirebirdandInterbase)
ByusingthiscomponentyouareabletointercepteventstriggeredbystoredproceduresofaFirebird
databaseandreacttothem.Youonlyhavetoregistertheevent'stext(string)youwanttoreacttoinproperty
Eventswhichisastringlist.Youcandothisusingobjectinspectororinsidethesourcecode.Theeventalerter
isactivatedbyregisteringthelistedevents.TodothisyoushouldcalltheRegistereventsmethodbecause
thePropertiesAutoRegisterandRegistereddonotworkproperly.Ifyouoncehaveregisteredtheeventsthe
componentisabletoreacttothem.Alleventsareunregistered(deleted)bycallingmethodUnregisterEvents
andtheeventalertetisturnedoff.

Registrationofeventsand"activation"oftheeventalerter:
:
EventAlerter.Events.Add ('Minimum Stock Level Reached');
EventAlerter.Events.Add ('Credit Limit Exceeded');
EventAlerter.RegisterEvents;
:

Master/DetailwithZEOSLibrary
ZEOSDataSetcomponentscomewithtwokindsofmaster/detailconnections:thosewithaserversidedfilter
andthosewithaclientsidedfilter.BothkindsandonekindinadditiointhatisindependentfromZEOS(and
thuswithoutanycomfort)willbedescribedhere.

Note:Ifwetalkabout"master"or"detail"thenaTDataSetdescendant(TZQuery/TZReadOnlyQuery,
TZTableoraTZStoredProc)ismeantthataccesseseitheramasterresultsetoradetailresultsetofa
master/detailconnection.
Master/Detailwithserversidedfilters

ThismethodisthedefaultbehaviouroftheBDE'sTQuerycomponent.Amaster/detailconnectionoftwo
DataSetsisestablishedasfollows:
Themaster'sDataSourceisassignedtotheDataSourceofthedetail.
Allprimarykeyfieldsofthemasterhavetobecomparedwiththeforeignkeyfieldsofthedetailinthe
detailSQLstatement.

Thisisanexampleforasimplemaster/detailqueries.Requirement:WeuseTZQueryorTZReadOnlyQuery
toestablishthemaster/detailconnection:
MasterSQL:

SELECT id, feld1, feld2, feld3


FROM master

[*]DetailSQL:

SELECT feld1, feld2, master_id


FROM detail
WHERE master_id = :id

Parameter:idstandsforthecontentofmaster's"id"field(istheprimarykey)becausethemaster's
DataSourceisassignedtothepropertyDataSourceofthedetail.Sothisparameterreferencestothe"id"
fieldofthemaster.Therield"master_id"indetailqueryistheforeignkeyfieldofthedetailtablewhich
referencestheprimarykeyofthemaster.

Ifthecursorofthemasterchangesitspositionwhileserversidedfiltersareused,theSQLstatementofthe
detailisexecutedusingthecurrentkeyvalues.Sotheresultsetofthedetailisautomaticallyrefreshed.

Master/Detailwithclientsidedfilters

ThisisthedefaultbehaviourofaBDETTablecomponent.Hereamaster/detailconnectionbetweentwo
DataSetsisestablishedasfollows:
TheDataSourceofthemasterisassignedtothepropertyMasterDataSourceofthedetail.
TheprimarykeyfieldsofthemasterareassignedtopropertyMasterFieldofthedetail.
Theforeignkeyofthedetailwhichreferencestheprimarykeyofthemasterisassignedtopropety
IndexFieldNames.

Thisisanexampleforasimplemaster/detailqueries.Requirement:WeuseTZQueryorTZReadOnlyQuery
toestablishthemaster/detailconnection:
MasterSQL:

SELECT id, feld1, feld2, feld3


FROM master
DetailSQL:

SELECT feld1, feld2, master_id


FROM detail
WHERE master_id = :id

Parameter:idstandsforthecontentofmaster's"id"field(istheprimarykey)becausethemaster's
DataSourceisassignedtothepropertyDataSourceofthedetail.Sothisparameterreferencestothe"id"
fieldofthemaster.Therield"master_id"indetailqueryistheforeignkeyfieldofthedetailtablewhich
referencestheprimarykeyofthemaster.

Ifthecursorofthemasterchangesitspositionwhileserversidedfiltersareused,theSQLstatementofthe
detailisexecutedusingthecurrentkeyvalues.Sotheresultsetofthedetailisautomaticallyrefreshed.

Master/Detailwithclientsidedfilters

ThisisthedefaultbehaviourofaBDETTablecomponent.Hereamaster/detailconnectionbetweentwo
DataSetsisestablishedasfollows:
TheDataSourceofthemasterisassignedtothepropertyMasterDataSourceofthedetail.
TheprimarykeyfieldsofthemasterareassignedtopropertyMasterFieldofthedetail.
Theforeignkeyofthedetailwhichreferencestheprimarykeyofthemasterisassignedtopropety
IndexFieldNames.

WithclientsidedfiltersbothDataSetsfirsttransferalltablerowsfromservertoclient.Thedetailthensetsa
filter(onclientside)togetthedetailsaccordingtothecurrentmasterrecord.

Incaseofcreatinganewdetailrecordforamaster/detailconnectionwithaclientsidedfilterthereisakindof
automatism:TheForeignkeyfieldsofthedetail(setinpropertyIndexFieldNamesofthedetail)willbefilled
automaticallywiththeaccording(current)primarykeydataofthemaster(setinpropteryMasterFieldofthe
detail).Note:Withserversidedfiltersyouhavetocareaboutthisfunctionality,manuallyinyourprogram's
code.ThiscanbeachievedbyimplementingtheOnNewRecordeventofthedetail.Thiseventisalways
triggeredwhenannewrecordistobecreated(see:DelphionlinehelpforTDataSet).AccordingtotheSQL
statements,definedaboveyouonlyhavetoimplementthefollowing:
Procedure dmMasterDetail.qryDetailNewRecord (DataSet: TDataSet);
Begin
qryDetailMASTER_ID.Value := qryMasterID.Value;
End;

CorrespondingTFieldswerecreatedforthefields"master_id"ofthedetailand"id"ofthemasterusingthe
fieldeditor.

Formaster/detailconnectionsinZEOSthereisanadditionaloptionthatissetinpropertyOptions:Itis
doAlwaysResyncDetail.Ifthisoptionissetthentheresultsetofthedetailisonlyrefreshedwhenpostis
calledorarecordchanges(bothwithinmasterDataSet).

Master/Detail"byhand"

Normallyyouimplementamaster/detailconnectionaccordingtothemethodusedbyserversidedfilters.The
SQLstatementsforthislookexactlylikethat.Onlythepropertiesthataresetinmasteranddetail(see
above)willnotbesethere.BothTZQuerisareworkingindependently.Thismeans:ThedetailDataSetdoes
notrecognizeanychangesinmasterDataSet.Itssynchronzationhastobeimplemented,manually.Thiswill
bedoneintheOnChangeeventofthemaster.OnChangeistriggeredwhenchangingtoanewrecordor
fielddatahasbeenchanged(see:DelphionlinehelpforTDataSource).Synchronizationofthedetail
(accordingtotheexampleabove)wouldbeimplementedlikethis:
Procedure dmMasterDetail.dsMasterDataChange (
Sender: TObject; Field: TField);
Begin
With qryDetail do Begin
Close;
ParamByName('id').Value := qryMasterID.Value;
Open;
End;
End;
ThisisthesamefunctionthatZEOSexecutesautomaticallywhenusingserversidedfilters:Thedetailquery
isexecuted(agin)withthecurrentIDvalueofthemasterwhichwillbeassignedtoparameter":id"inthe
detailquery.Sotheresultsetofthedetailwillberefreshedaccordingtothecurrentmasterrecord.

IfanewrecordshallbecreatedindetailDataSetthenyouhavetoactthesamewayaswithserversided
filters(seeabove).

CachedUpdates
ThedevelopersoftheZEOSLibraryareintendedtoimplementthefunctionalityoftheBDEcomponentsas
goodaspossible.ThisiswhythereisalsothepossibilityofcachedupdateswithZEOS.Youonlyhavetoset
thepropertyCachedUpdatesofaDataSetdescendant(TZTable,TZQueryoderTZStoredProc)totrue.From
thistimeonallchangesintheresultsetwillbecachedandtheycanbeeasilycommittedtothedatabaseby
callingApplyUpdatesandCommitUpdatesoneaftertheothereitherautomaticallyinyourprogramcodeor
triggeredmanuallybyauser.CancelUpdatescausesthatthecangeswillnotbecommittedtothedatabase.
Inthiscaseallcachedchangeswillbereset(likeusingaROLLBACK).Hereyouwillfindalittlecodesnippet
thattriestoshowyouhowcachedupdatesareimplemented(don'targueaboutthesenseinthis...).
AutoCommitmodeofTZConnectinisturnedon(True):
:
With DataSet do Begin

bEverythingOK := True;
CachedUpdates := True;
DataSet.First;

While (not DataSet.EOF) and (bEverythingOK) do Begin


DataSet.Edit;
:
// process record
:
DataSet.Post;
DataSet.Next;
:
bEverythingOK := AFunctionForValidation;
End;

If bEverythingOK Then Begin


ApplyUpdates;
CommitUpdates;
End
Else
CancelUpdates;

CachedUpdates := False;
End;
:
BLOBFields
AccordingtoBDE'scomponentsthecomponentsoftheZEOSLibraryarecapableofhandlingBLOBfields.
HereisanexamplehowanewrecordwithaBLOBfieldiscreated.TheBLOBfieldisfilledwithabitmap.To
achievethiswehavetouseaStream:
:
Var TheStream : TMemoryStream;
Begin
TheStream := TMemoryStream.Create;
Try
Image1.Picture.Bitmap.Savetostream(TheStream);
With qryBlobInsert do Begin
Sql.Text := 'INSERT INTO EVENTS (EventNo,EVENT_PHOTO) ' +
'VALUES (100,:ThePicture)';
Params.Clear;
Params.CreateParam(ftBlob,'ThePicture', ptInput);
ParamByName('ThePicture').LoadfromStream(TheStream,ftBlob);
ExecSQL;
End;
Finally
TheStream.Free;
End;
End;
:

Thissectionaboutblobfieldsisnotcompleted,yet.PleasefeelfreetosendmeaprivatemessageoreMailif
youhaveanyissuesaboutblobsinordertoexpandthissection.

Sampleproject:"EasyQuery"
TopreventbeingtootheoreticalnowwewillcreateasmallsampleprojectthodemonstratehowtheZEOS
componentsareusedingeneral.Wewillcreateasmallapplicationthataccessesthetables"Customer"and
"Country"oftheFirebirdsampledatbase"Employee".Youshouldbeabletonavigateintable"Customer"
andedititsdata.AndnotbeintooboringwewillimplementaDBLookupComboboxforfield"Country"intable
"Customer".Thisfieldisaforeignkeythatreferencestofield"Country"intable"Country".

Solet'sgetstarted!

FirstofallwehavetocreateanewprojectinDelphi.Additionalytothedefaultformwehavetocreatea
DataModule.

ThefollowingpropertiesoftheDataModulehavetobeset:
Name:dmEasyQuery

ComponentsfortheDataModule
TZConnection

Database:Employee.fdb
Name:conEmployee
Password:"password"
Protocol:firebird1.5
ReadOnly:False
TransactionIsolationLevel:tiReadCommitted
User:"username"

TZQuery

Connection:conEmployee
Name:qryCustomer
RequestLive:True
SQL:SELECT*FROMcustomerORDERBYcustomer
UpdateObject:updCustomer

Note:YouhavetocreatepersistentTFieldsforalltablefieldsusingthefieldeditor!

TheOnAfterPosteventofqryCustomerwillbeimplementedlikethis:
procedure TdmEasyQuery.qryCustomerAfterPost(DataSet: TDataSet);
begin
// Refresh of resultset to actualize (sort) data shown in DBGrid.
qryCustomer.Refresh;
end;

TZReadOnlyQuery

Connection:conEmployee
Name:roqryCountry
SQL:SELECTcountryFROMcountryORDERBY1

Note:YouhavetocreatepersistentTFieldsforalltablefieldsusingthefieldeditor!

TZUpdateSQL

DeleteSQL:createdbyUpdateSqleditor
InsertSQL:createdbyUpdateSqleditor
ModifySQL:createdbyUpdateSqleditor
Name:updCustomer

Thedelete,insertandmodifyStatementsfortheTZUpdateSQLobjectarecreatedwiththeUpdateSQL
editor,automatically.TheeditorwillbeactivatedbydoubleclickingontheTZUpdateSQLcomponent.Askey
fieldyouhavetoselectthefieldCUST_NO.ThefieldsADDRESS_LINE1,ADDRESS_LINE2,
CITY,STATE_PROVINCE,COUNTRYundPOSTAL_CODEwillselectedasupdatefieldsinlist"Update
Fields".Nowthestatementswillbegeneratedbyclickingbutton"GenerateSQL".Ifyouonlyhavesucheasy
queriesyoucansavealotoftypingbyusingtheUpdateSQLeditor.Ifitgetsalittlemorcomplexyoushould
createtheneededstatements"byhand".

TDataSource

DataSet:sqlCustomer
Name:dsCustomer

TDataSource

DataSet:rosqlCountry
Name:dsCountry

NowthecreatedDataModulewillbesavedasdm_EasyQuery.pas.[/list]
Componentsfortheform

Themainformwillhavethefollowingcomponentsandisinitializedasfollows:

Properties

Caption:EasyQueryDemo
Name:frmEasyQuery

IntheUnit'sinterfaceyouhavetoadddm_EasyQuerytotheusesclausetogetaccesstothe
databasecomponents.

Thefollowingeventsofthemainformhavetobeimplemented::

OnCreate
Whencreatingtheformtheconnectiontothedatabasewillbeestablished.Afterconnectingtothe
databasethequerieswillbeopened:
procedure TForm1.FormCreate(Sender: TObject);
begin
dmEasyQuery.conEmployee.Connect;
dmEasyQuery.qryCustomer.Open;
dmEasyQuery.roqryCountry.Open;
end;

OnDestroy
Whenclosingtheapplication(destroyingthemainform)thedatabaseconnectionwillbecut.All
querieswillbeclosedautomaticallybeforedisconnecting.
procedure TForm1.FormDestroy(Sender: TObject);
begin
dmEasyQuery.conEmployee.Disconnect;
end;

TLabel

Caption:CUSTOMER
TDBGrid

DataSource:dmEasyQuery.dsCustomer
Options.dgTabs:False

IncolumneditorwewillcreateaTColumnsettingitspropertyFieldNameto"CUSTOMER".Afterthat
theaccordingcolumninDBGrid1willbeenlarged.

TDBEdit

(5x)

TLabel]

(5x)

TheseobjectswillbecreatedbyusingthecolumneditorofqryCustomer:Selectcolumns
ADDRESS_LINE1,ADDRESS_LINE2,CITY,STATE_PROVINCEandPOSTAL_CODEanddragand
dropthemontothenmainform.Alignthemandadaptthemtothelayoutyouseeinthescreenshot.

TLabel

Caption:COUNTRY

TDBLookUpComboBox

DataField:COUNTRY
DataSource:dmEasyQuery.dsCustomer
KeyField:COUNTRY
ListField:COUNTRY
ListSource:dmEasyQuery.dsCountry

TDBNavigator

DataSource:dmEasyQuery.dsCustomer

Nowthecreatedformwillbesavedasfrm_EasyQuery.pas.
Note:Thefollowingprojectoptionshavetobechanged:

dmEasyQueryhastobeplacedontopofthecreationorderinlist"Createautomatically".This
ensuresthatallobjectsoffrmEasyQuerymayaccessthedatabaseobjects.frmEasyQueryisstill
themainform.

AdditionalExamples
FishFact
ThisisthemostpopulardatabasedemoforDelphi.ItwasmigratedtouseZEOScomponents.

Transactions
Asampleapplicationconcerning"transactionswithZEOS".Itusesasmallselfmadetestdatabase.

StoredProc
Thisisasampleapplikationthatshowshowtousestoredproecedures.ThedatabaseistheFirebird
Employeesampledatabase.

MasterDetail
Asmallapplicationthatshowshowmaster/detailconnections(serverandclientsided)willbeimplemented.

EventDemo
AlsoaDelphidatabasesamplethatwasmigratedfromIBXtoZEOS.ItusesthecomponentTIBEventAlerter.
ThisapplicationneedsthedatabaseEvents(shippedwithDelphi)thathadtobemigratedtodialect3toget
thissamplerunning.

YouwillfindtheseExamplesherefordownload.
AdownloadablePDFVersionofthisTutorialisalsoonitsway!
MichaelSeeger
ZeosLibDevelopmentTeam

You might also like