Documente Academic
Documente Profesional
Documente Cultură
Optimisation des accs Base de donnes, IIIme Partie Par Franck SORIANO
Cet article prsente comment utiliser directement OLEDB pour excuter une requte sur une base de donnes. SQL Server est utilis pour les exemples, mais ce tutoriel peut s'appliquer n'importe quel SGBD. Grce OLEDB et la classe TMemoryDataSet prsente dans l'article prcdent, on peut obtenir des performances quatre fois suprieures qu'avec une application ADO traditionnelle ou dbExpress. Commentez cet article :
I - Introduction..............................................................................................................................................................3 Tlcharger les sources de l'article....................................................................................................................... 3 I-B - Organisation du codes source....................................................................................................................... 3 II - Mise en oeuvre de OLEDB................................................................................................................................... 4 II-A - Connexion/Dconnexion............................................................................................................................... 4 II-A-1 - La classe TOleDbConnection...............................................................................................................4 II-A-2 - Ouvrir la connexion : DoConnect......................................................................................................... 4 II-A-3 - Deconnexion : DoDisconnect............................................................................................................... 7 II-A-4 - Savoir si on est connect : GetConnected.......................................................................................... 7 II-A-5 - Comment construire une chane de connexion ?................................................................................ 8 II-B - Gestion des Erreurs OLEDB et messages d'informations............................................................................9 II-C - Excuter une requte ne renvoyant aucun rsultat................................................................................... 14 II-D - Excuter une requte de type SELECT..................................................................................................... 16 II-D-1 - Lecture de la structure du jeu de donne : Describe.........................................................................18 Remarque sur les types de donnes utiliss............................................................................................21 II-D-2 - Chargement des donnes : FetchAll..................................................................................................21 II-D-2-a - Dfinition du Binding..................................................................................................................21 II-D-2-b - Lecture des donnes................................................................................................................. 25 II-E - Requtes paramtres................................................................................................................................28 II-F - Prparer les Requtes................................................................................................................................ 33 II-G - Gestion des Transactions........................................................................................................................... 34 II-G-1 - Dmarrer une transaction : StartTransaction..................................................................................... 34 II-G-2 - Valider une transaction : Commit....................................................................................................... 35 II-G-3 - Annuler une transaction : Abort......................................................................................................... 36 III - Evaluation des rsultats et exemples d'utilisations............................................................................................ 37 III-A - Environnement de tests............................................................................................................................. 37 III-B - Connexion/Dconnexion............................................................................................................................ 37 III-C - Excuter une requte................................................................................................................................ 39 III-D - Excuter une requte paramtre.............................................................................................................40 III-E - Messages d'informations............................................................................................................................40 III-F - Lire un fichier EXCEL................................................................................................................................ 41 IV - Conclusion.......................................................................................................................................................... 41 V - Rfrences...........................................................................................................................................................41 VI - Remerciements................................................................................................................................................... 41
-2Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
I - Introduction
OLEDB a t conu pour remplacer ODBC et uniformiser les accs aux bases de donnes. En fait, OLEDB va bien au del et permet d'accder toute source de donnes pour laquelle on dispose d'un provider OLEDB. Il peut s'agir d'un SGBD, mais galement d'un fichier Excel... OLEDB est une API bas niveau, conue pour donner les performances optimales. Cependant, ces performances viennent au dtriment de la facilit d'utilisation. Aussi, Microsoft a dfini ADO par dessus OLEDB afin de simplifier son usage. Dans Delphi, cette couche ADO est elle-mme encapsule dans les composants dbGO. Or comme on a pu le voir avec le comparatif sur les API d'accs aux donnes, cet empilement de couches dgrade les performances de faon significative. Pour retrouver les performances originelles, Il faut appeler OLEDB directement, en court-circuitant la couche ADO et l'encapsulation dbGO. Dans cet article, nous allons voir les principes d'utilisation d'OLEDB. Nous allons dvelopper nos propres composants d'accs aux donnes afin d'utiliser facilement OLEDB dans nos applications Delphi. Nous allons effectuer tous les dveloppements avec SQL Server 2005. Cependant, OLEDB tant une API gnrique, rien n'interdit d'ouvrir la connexion sur un autre SGBD...
-3Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
On peut ainsi instancier l'objet COM. Ce dernier retourne une interface IDBInitialize permettant d'ouvrir la connexion. Cependant, avant d'ouvrir la connexion, il faut commencer par dfinir les proprits de l'objet (Login, password, nom du serveur...). Les proprits initialiser, ainsi que les valeurs dfinir sont spcifiques chaque provider. Si on garde cette approche, le code va vite devenir difficilement maintenable. La procdure de connexion est spcifique au provider, donc au SGBD. Pour une API gnrique, ce n'est pas ce qu'il y a de mieux.
-4Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
Pour simplifier le problme, OLEDB dfinie et implmente un service indpendant des providers. Il s'agit du service DataLink. Ce dernier n'est rien d'autre qu'une Factory pour les providers. Il permet d'instancier et d'initialiser n'importe quel provider partir d'une chane de connexion, comme pour ADO. L'ouverture de la connexion est alors grandement facilite puisque tout est dfini dans la chane de connexion : On commence par instancier un objet CLSID_MSDAINITIALIZE. Il s'agit du service OleDb pour le Data link. Ce dernier implmente l'interface IDataInitialize. Elle existe dans deux versions pour Delphi, une version qui utilise la convention SafeCall (IDataInitializeSC), et une version avec la convention StdCall. La version SafeCall gre automatiquement les codes de retour HResult. C'est elle que nous utilisons :
var FDataInitialize : IDataInitializeSC; ... OleCheck(CoCreateInstance(CLSID_MSDAINITIALIZE, nil, CLSCTX_INPROC_SERVER, IID_IDataInitialize, FDataInitialize));
Lorsqu'on dispose d'IDataInitialize, il n'y a plus qu' demander l'objet DataSouce (le provider) dfinit dans la chane de connexion. C'est ce qu'on fait en appelant la mthode GetDataSource.
var Unknown : IUnknown; FDbINitialize : IDbInitialize; ... FDataInitialize.GetDataSource(nil, CLSCTX_INPROC_SERVER, PWideChar(FConnectionString), IID_IDBInitialize, Unknown); FDbInitialize := Unknown as IDBInitialize;
On obtient alors une interface IDbInitialize, qui rfrence un objet DataSource instanci et pr-initialis. Cependant, la connexion la base n'est pas ouverte pour autant. Il faut encore l'initialiser en appelant Initialize, et ouvrir une session avec la mthode CreateSession de l'interface IDBCreateSession :
// On ouvre la connexion sur la source de donne. OleDbCheck(FDbInitialize.Initialize); // Il ne reste plus qu' crer une session par dfaut. FSession := nil; OleDbCheck((FDbInitialize as IDBCreateSession).CreateSession(nil, IID_IOpenRowset, FSession));
Rassemblons le tout dans la mthode DoConnect. On en profite au passage pour tracer l'ouverture de la connexion avec ETW.
procedure TOleDbConnection.DoConnect; var Unknown : IUnknown; FDataInitialize : IDataInitializeSC; begin // Tout d'abord, la chane de connexion doit avoir t dfinie if FConnectionString = '' then raise EOleDbException.Create('La chane de connexion n''est pas dfinie !'); SQLLogger.Trace(EVENT_SQL_INFO, 'Ouverture de la connexion : ' + FConnectionString, TRACE_LEVEL_INFORMATION); try // Enfin, on a besoin d'un accs IMAlloc pour grer certaines allocation // de mmoire. OleCheck(CoGetMalloc(1, FMAlloc)); // L'initialisation de la connexion est faite indirectement, // par l'intermdiaire du service OLEDB grant les chanes de connexion. // On commence donc par se connecter ce service, en instanciant un // objet CLSID_MSDAINITIALIZE. OleCheck(CoCreateInstance(CLSID_MSDAINITIALIZE, nil, CLSCTX_INPROC_SERVER, IID_IDataInitialize, FDataInitialize));
-5Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
// Dans un deuxime temps, on cre l'objet DataSource oledb partir // de la chane de connexion. L'objet ainsi cr est dj initialis. // Il ne restera plus qu' ouvrir la connexion. Unknown := nil; FDataInitialize.GetDataSource(nil, CLSCTX_INPROC_SERVER, PWideChar(FConnectionString), IID_IDBInitialize, Unknown); FDbInitialize := Unknown as IDBInitialize; FDataInitialize := nil; // On n'a plus besoin d'accder au service ! // On ouvre la connexion sur la source de donne. OleDbCheck(FDbInitialize.Initialize); // Il ne reste plus qu' crer une session par dfaut. FSession := nil; OleDbCheck((FDbInitialize as IDBCreateSession).CreateSession(nil, IID_IOpenRowset, FSession)); // On essaie d'obtenir l'interface ITransactionLocal pour la gestion des // transaction. Cependant, il se peut que le provider OLEDB ne gre pas les // transaction et n'implmente pas l'interface ITransactionLocal. if FSession.QueryInterface(IID_ITransactionLocal, unknown) = S_OK then FTransaction := unknown as ITransactionLocal else FTransaction := nil; // La connexion a t effectue. SQLLogger.TraceConnect; except on e:exception do begin // En cas d'erreur la connexion, on trace l'exception. SQLLogger.TraceException(e); // Puis on nettoie les interfaces. FSession := nil; // La connexion a chou. FDbInitialize := nil; FDataInitialize := nil; // On redclenche l'exception. raise; end; end; end;
Remarque : DoConnect fait galement appel une mthode OleDbCheck. Cette dernire est utilise pour la gestion des erreurs et sera explique plus loin.
-6Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
-7Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
http://fsoriano.developpez.com/articles/db/oledb/delphi/
Ca vous rappelle quelque chose ? Et oui, c'est exactement la bote de dialogue ADO. En fait, ADO n'tant qu'une surcouche par-dessus OLEDB, c'est ADO qui utilise le dialogue OLEDB.
-9Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
Tout d'abord, nous devons faire la gestion des erreurs nous mme, et ne pas utiliser la gestion par dfaut faite par Delphi. Cela implique qu'on ne doit utiliser que les interfaces dfinies avec la convention d'appel stdcall. Chaque appel d'une mthode d'un objet COM retourne un code de retour : Le HResult. Ce dernier est encod d'une faon particulire. Le bit de poids fort est positionn 1 pour indiquer une erreur et 0 en cas de succs. Delphi fournit la fonction Succeeded pour tester si un Hresult donn indique une erreur. En cas d'erreur, il faut appeler la fonction GetErrorInfo pour obtenir les informations relatives l'erreur. Cette fonction renvoie une interface IErrorInfo avec le code et le message d'erreur. Ca c'est la gestion standard des exceptions COM. Comme on peut le voir, il n'est pas possible d'obtenir une liste d'erreur de cette manire. Avec OLEDB, une fois qu'on a obtenu l'interface IErrorInfo, il suffit en fait de s'en servir pour obtenir l'interface IErrorRecords. Cette dernire permet tout simplement d'obtenir une liste d'erreurs IErrorInfo. Cependant IErrorInfo peut ne pas tre suffisant pour dcrire compltement une erreur. C'est pourquoi OLEDB prvoit galement la mthode GetCustomErrorObject pour que chaque provider puisse fournir sa propre description de l'erreur. A ce titre, les providers SQL Server dfinissent l'interface ISQLServerErrorInfo. Cette dernire sert identifier le message d'erreur, mais aussi le numro de l'erreur, le numro de la ligne o elle s'est dclenche, ainsi que la gravit de l'erreur. Pour SQL Server, le niveau de gravit est trs important. En effet, une commande SQL peut retourner des erreurs, mais galement des messages d'information qui ne sont pas des erreurs, mais qui sont malgr tout renvoys sous la forme d'une erreur de gravit faible. Par exemple, si on fait un PRINT dans un script, le message du script remonte dans OLEDB sous la forme d'une erreur de gravit 0. Pour que les choses soient encore un peu plus complexes, pour un PRINT, le message remonte sous la forme d'une erreur, mais le hResult de l'appel ayant dclench le PRINT indique lui que tout s'est bien pass... De plus, un mme appel peut trs bien retourner la fois des messages d'informations et des erreurs. Ainsi, pour grer correctement les erreurs avec SQL Server, il faut ignorer la valeur du hResult et toujours essayer de lire la description complte des erreurs. Pour chaque erreur, il faut tester le niveau de gravit pour savoir s'il s'agit d'une erreur ou d'un message d'information. C'est la gestion que nous allons implmenter avec la mthode OleDbCheck.
procedure TCustomOleDbConnection.OleDbCheck(hResult : integer); var ErrInfo : IErrorInfo; ErrorInfo2 : IErrorInfo; Errors : IErrorRecords; nbError : cardinal; i : integer; Description : WideString; ErrMsg : widestring; ProcedureName : widestring; msg : string; unknown : IUnknown; SQLServerErrorInfo : ISQLServerErrorInfo; SQLServerError : PSSERRORINFO; SQLServerErrorMessage : PWideChar; begin ErrMsg := ''; // Premirement, on regarde si des informations sont disponibles sur l'erreur en cours. GetErrorInfo(0, ErrInfo); if Assigned(ErrInfo) // On a russit obtenir des informations. then begin // OLEDB tend la gestion des exceptions COM de faon retourner une liste d'exceptions // (la liste des exceptions l'origine de l'erreur) au lieu d'une seule et unique erreur. // Si on veut connaitre la cause de l'erreur, il faut lire la liste complte. Errors := ErrInfo as IErrorRecords;
- 10 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
// Maintenant, il ne reste plus qu' parcourir la liste des erreurs. Errors.GetRecordCount(nbError); for i := 0 to nbError-1 do begin // Si on travaille avec SQL Server, on peut obtenir des informations plus compltes sur // les erreurs avec l'interface ISQLServerErrorInfo. On commence donc par rechercher si // ces informations sont disponibles. Si le provider OLEDB n'est pas un provider SQL Server, // l'interface ISQLServerErrorInfo ne sera pas disponible. unknown := nil; Errors.GetCustomErrorObject(i, IID_ISQLServerErrorInfo, unknown); if Assigned(unknown) then begin SQLServerErrorInfo := unknown as ISQLServerErrorInfo; SQLServerError := nil; SQLServerErrorMessage := nil; if SQLServerErrorInfo.GetErrorInfo(SQLServerError, SQLServerErrorMessage) = S_OK then begin if Assigned(SQLServerError) then begin try // A prsent, on construit le message d'erreur partir des informations : // Le champ bClass indique le niveau de gravit de l'erreur. Les valeurs // infrieures 10 indiquent qu'il s'agit d'un simple message d'information. // Les valeurs suprieures 10 dsignent les erreurs. if SQLServerError.bClass <= 10 // Il s'agit d'un message d'information then begin // On ajoute le message la liste des messages d'information. InfoMessages.Add(SQLServerError.pwszMessage); end else begin // Il s'agit d'une erreur. On met en forme le message d'erreur dans msg. msg := 'MSG-' + IntToStr(SQLServerError.lNative) + ', '; // Numro de l'erreur // Traitement du nom de la procdure stocke l'origine de l'erreur ProcedureName := SQLServerError.pwszProcedure; if ProcedureName<>'' then msg := ProcedureName + ', '; // Ajout du numro de ligne de l'erreur msg := msg + 'Line ' + IntToStr(SQLServerError.wLineNumber) + ', '; // Enfin, on termine avec le message de l'erreur. msg := msg + SQLServerError.pwszMessage; // Il s'agit d'une erreur. On l'ajoute au message complet des erreurs. ErrMsg := ErrMsg + msg + #13#10; end; finally if Assigned(SQLServerError) then FMAlloc.Free(SQLServerError); if Assigned(SQLServerErrorMessage) then FMAlloc.Free(SQLServerErrorMessage); end; end; end; end else begin // Il ne s'agit pas d'une erreur SQLServer, on traite l'erreur de faon classique. // On commence par lire l'erreur ni dans ErrorInfo2 OleCheck(Errors.GetErrorInfo(i, GetSystemDefaultLCID, ErrorInfo2)); OleCheck(ErrorInfo2.GetDescription(Description)); ErrMsg := ErrMsg + Description + #13#10; end; end; end; // // // if Enfin, il ne reste plus qu' dclencher l'exception en cas d'erreur. Normalement, s'il y a eu une erreur, hResult doit contenir un code d'erreur et ErrMsg la description de l'erreur. (ErrMsg<>'') or (not Succeeded(hResult))
- 11 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
then begin if ErrMsg<>'' then raise EOleDbException.Create(ErrMsg) // On dclenche une erreur spcifique OLEDB else raise EOleSysError.Create('', hResult, 0); // On dclenche une erreur systme OLE end; end;
L'interface IErrorRecords est spcifie par OLEDB. Cependant l'unit OleDb de Delphi ne contient pas sa dclaration (probablement parce que cette dernire n'est pas compatible OLE Automation). Nous devons donc la dclarer nous mme :
type // L'interface IErrorRecords fait parti de OLEDB. Cependant l'unit OleDb.pas ne la dclare // pas. On doit donc la dclarer nous-mme. IErrorRecords = interface(IUnknown) ['{0c733a67-2a1c-11ce-ade5-00aa0044773d}'] function AddErrorRecord(pErrorInfo : PErrorInfo; dwLookupID : cardinal; pdispparams : pointer; punkCustomError : IUnknown; dwDynamicErrorID : cardinal) : HResult; stdcall; function GetBasicErrorInfo(ulRecordNum : cardinal; pErrorInfo : PErrorInfo) : HResult; stdcall; function GetCustomErrorObject(ulRecordNum : cardinal; const riid : TGUID; var ppObject : IUnknown) : HResult; stdcall; function GetErrorInfo(ulRecordNum : cardinal; lcid : cardinal; var ppErrorInfo : IErrorInfo) : HResult; stdcall; function GetErrorParameters(ulRecordNum : cardinal; pdispparams : pointer) : HResult; stdcall; function GetRecordCount(var pcRecords : cardinal) : HResult; stdcall; end;
De mme l'interface ISQLServerErrorInfo n'est dfinie que dans le fichier entte de SQL Native Client. On doit donc galement la dclarer nous mme :
ISQLServerErrorInfo = interface(IUnknown) ['{5CF4CA12-EF21-11d0-97E7-00C04FC2AD98}'] function GetErrorInfo(out ppErrorInfo : PSSERRORINFO; out Error : PWideChar) : Hresult; stdcall; end;
- 12 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
Lors des appels aux mthodes OLEDB, il ne restera plus qu'a tester chaque code de retour avec OleDbCheck. En cas d'erreur, OleDbCheck dclenche une exception avec la description complte de l'erreur. Les messages d'informations sont filtrs et ajouts la fin de la liste InfoMessages de TOleDbConnection. Cette liste sera vide chaque fois qu'on excute une nouvelle requte avec OpenSQL ou ExecSQL. Au final, OleDbCheck est capable de traiter n'importe quelle erreur issue d'un provider OLEDB quelconque. En revanche, elle a t enrichie pour traiter galement les spcificits de SQL Server. Si on veut faire un composant spcialis pour un autre provider, on devra srement traiter d'autres interfaces tendues pour la gestion des erreurs. Dans ce cas, il sera sans doute intressant de rendre la mthode OleDbCheck virtuelle, afin qu'elle puisse tre surcharge dans un composant spcialis.
- 13 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
Puis, on initialise le traitement de la commande avec la requte SQL. Il suffit d'utiliser la mthode SetCommandText de l'interface ICommandText :
cmd := unknown as ICommandText; cmd.SetCommandText(DBGUID_DEFAULT, PWideChar(SQL));
DbParams est une structure TDBParams permettant de spcifier les paramtres de la requte. Comme cette dernire n'est pas paramtre, on l'initialise de la faon suivante :
// Il n'y a pas de paramtres DbParams.pData := nil; DbParams.cParamSets := 0; DbParams.HACCESSOR := 0;
IID_NULL est un guid spcial indiquant le type d'interface qu'on souhaite obtenir en retour pour lire les rsultats de la requte. Comme la requte ne doit pas renvoyer de donnes, cette valeur permet d'indiquer qu'on n'attend rien en retour. Il faut le dfinir de la faon suivante :
const IID_NULL: TGUID = '{00000000-0000-0000-0000-000000000000}';
Il ne reste plus qu' runir le tout dans une mthode ExecSQL sur notre objet TOleDbConnection :
// Execute une commande SQL qui ne retourne pas de donnes. // C'est la mthode la plus simple pour excuter une requte SQL. procedure TOleDbConnection.ExecSQL(const SQL: widestring; Params : TParams); var t0 : int64; cmd : ICommandText; unknown : IUnknown; OleDbParams : TOleDbParams; begin // Premirement, on trace le dbut de la requte. SQLLogger.TraceSQLBegin(SQL, t0); try try CheckConnected; // On s'assure que la connexion est ouverte. InfoMessages.Clear; // On rinitialise la liste des messages d'information
- 14 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
// Tout d'abord, il faut crer un objet Command pour excuter la requte. OleDbCheck((FSession as IDBCreateCommand).CreateCommand(nil, IID_ICommandText, unknown)); // Ensuite on initialise la requte SQL. cmd := unknown as ICommandText; cmd.SetCommandText(DBGUID_DEFAULT, PWideChar(SQL)); OleDbParams := TOleDbParams.Create(Params, cmd, self); try // Enfin on excute la commande, sans attendre de rsultats. OleDbCheck(cmd.Execute(nil, IID_NULL, OleDbParams.Parameters, nil, nil)); // On met jour la valeur des paramtres de sorti OleDbParams.UpdateParams(Params); finally OleDbParams.Free; end; except on e:exception do begin // En cas d'erreur, on trace l'exception. SQLLogger.TraceException(e); raise; // Et on redclenche l'erreur. end; end; finally SQLLogger.TraceSQLEnd(SQL, t0); // Pour finir, on logue la fin de la requte end; end;
Bien videmment, on n'oublie pas d'instrumenter la mthode pour tracer l'excution de la requte avec ETW. TraceSQLBegin et TraceSQLEnd vont tracer le dbut et la fin de l'excution. En cas d'erreur, l'exception est ellemme trace avec TraceException avant d'tre redclenche.
- 15 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
- 16 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
begin SQLLogger.TraceException(e); if Assigned(ds) then ds.Free; raise; end; end; finally // Pour finir, on trace la fin de la requte if Assigned(ds) then SQLLogger.TraceSQLEnd(SQL + ', ' + IntToStr(ds.RecordCount) + ' Lignes', t0) else SQLLogger.TraceSQLEnd(SQL, t0, Params); end; result := ds; end;
En fait, le code est identique, jusqu'au moment d'excuter la commande. L'excution de la commande s'effectue un peu diffremment :
// Excution de la requte sur la source de donnes. OleDbCheck(cmd.Execute(nil, IID_IRowset, DbParams, nil, @unknown)); RowSet := unknown as IRowSet;
Cette fois, au lieu d'utiliser IID_NULL, on demande une interface IRowSet avec IID_Rowset. C'est cette dernire qui va permettre de lire le rsultat. Ensuite, il faut crer le dataset qui sera renvoy. Nous allons retourner un objet TMemoryDataSet. Il s'agit du dataset en mmoire que nous avons dfinit dans l'article prcdent. Le chargement OLEDB va tirer parti de l'organisation interne des donnes dans TMemoryDataSet pour s'effectuer la vitesse grand V. Nous allons driver TMemoryDataSet en TCustomOleDbDataSet et TOleDbDataSet et lui dlguer la gestion de ce chargement :
// La source de donnes vient de rendre la main. On peut lire les donnes. ds := TOleDbDataSet(CreateDataSet); // On instancie le DataSet qui sera retourn. ds.LoadFromRowSet(unknown as IRowSet, self); // Lecture des donnes.
- 17 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
Open; // On doit ouvrir le dataset pour pouvoir charger les donnes. FetchAll(RowSet, cnt); // Charge les donnes du rowset dans le dataset. First; // On se positionne au dbut du DataSet. finally SQLLogger.TraceEnd(EVENT_SQL_END, ' Fin Fetch', t0); end; end;
Tout d'abord, la mthode est instrumente pour indiquer le dbut et la fin du traitement. De cette faon, on pourra obtenir des traces d'excution indiquant prcisment le temps pass l'excution de la requte et le temps pass lire les donnes. Ensuite, on peut voir que le chargement est fait en quatre tapes : Premirement, on dfinit les champs du DataSet en fonction du rsultat de la requte, c'est--dire en fonction de la structure des donnes dans RowSet. On effectue cette opration avec la mthode Describe dveloppe spcialement pour ce rle. Ensuite, on ouvre le DataSet. L'ouverture engendre la cration des TField et l'initialisation des buffers internes. A ce moment, le dataset est encore vide. Il ne reste alors plus qu' l'alimenter avec les donnes. Cette opration est faite avec la mthode FetchAll. Enfin, on fait un First pour repositionner le Dataset sur le premier enregistrement et recharger les buffers de la ligne en cours.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
En sortie, nbColumns indique le nombre de champs prsents dans RowSet, ColumnsInfo pointe sur un tableau de structures DBCOLUMNINFO et infos est un buffer contenant les noms des champs. Il ne reste plus qu' parcourir le tableau ColumnsInfo pour crer les TFieldDef du dataset :
procedure TCustomOleDbDataSet.Describe(RowSet: IColumnsInfo; Cnt : TCustomOleDbConnection); var nbColumns : cardinal; ColumnsInfo : PDBColumnInfo; infos : PWideChar; Column : PDBColumnInfo; columnName : widestring; DataType : TFieldType; DataSize : cardinal; i : integer; n : integer; IsNullable : boolean; begin // On appelle GetColumnInfo pour obtenir un tableau dcrivant la liste des colonnes prsentes. Cnt.OleDbCheck(RowSet.GetColumnInfo(nbColumns, ColumnsInfo, Infos)); try FieldDefs.BeginUpdate; Column := ColumnsInfo; // Pointe sur la description de la colonne en cours try FieldDefs.Clear; for i := 0 to nbColumns-1 do begin DataSize := 0; // Si la colonne n'a pas t nomme, on lui attribue un nom automatique // sous la forme colX avec X indiquant le numro de la colonne ( partir 1) if Assigned(Column.pwszName) then columnName := Column.pwszName else columnName := 'col' + IntToStr(i+1); if Trim(columnName) = '' then columnName := 'col' + IntToStr(i+1); // Ensuite il faut vrifier si le nom n'existe pas dj et renommer les // colonnes automatiquement en cas de besoin. if FieldDefs.IndexOf(columnName)<>-1 then begin n := 1; columnName := columnName + '_'; while FieldDefs.IndexOf(columnName + IntToStr(n))<>-1 do begin inc(n); end; columnName := columnName + IntToStr(n); end; // A prsent, on peut commencer crer les champs. case Column.wType of DBTYPE_I1, DBTYPE_UI1, DBTYPE_I2: // Entier court sign DataType := ftSmallint; DBTYPE_UI2: // Entier court non sign DataType := ftWord; DBTYPE_UI4, DBTYPE_I4: // Entier, sign ou non sign DataType := ftInteger; DBTYPE_CY: // Type currency. DataType := ftBCD; DBTYPE_DATE, DBTYPE_DBDATE, DBTYPE_DBTIME: // Dates DataType := ftDateTime; DBTYPE_DBTIMESTAMP: // DateTime prcis. DataType := ftTimeStamp; DBTYPE_BOOL: // Boolen DataType := ftBoolean; DBTYPE_R4, DBTYPE_R8, DBTYPE_DECIMAL, DBTYPE_NUMERIC: // Nombre dcimal DataType := ftFloat; DBTYPE_I8, DBTYPE_UI8: // Entier long
- 19 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
DataType := ftLargeint; DBTYPE_GUID: // GUID begin DataType := ftGuid; DataSize := 38; end; DBTYPE_BYTES, DBTYPE_UDT: // Binaire DataType := ftBlob; DBTYPE_STR: // Chane de caractres begin // Si la longueur du champ dpasse une certaine longueur, on en fait un memo. if Column.ulColumnSize<COLUMN_MAXSIZE then begin DataType := ftString; DataSize := Column.ulColumnSize; end else DataType := ftMemo; end; DBTYPE_WSTR: // Chane de caractres unicode UTF-16. begin // Si la longueur du champ dpasse une certaine longueur, on en fait un memo unicode. if Column.ulColumnSize <COLUMN_MAXSIZE then begin DataType := ftWideString; DataSize := Column.ulColumnSize; end else DataType := ftWideMemo; end; else begin raise EOleDbException.Create( Format('Le type (%d) de la colonne %s n''est pas support !', [Column.wType, columnName])); end; end; IsNullable := (ColumnsInfo.dwFlags and DBCOLUMNFLAGS_MAYBENULL) <>0; try TFieldDef.Create(FieldDefs, columnName, DataType, DataSize, not IsNullable, FieldDefs.Count); except on e : EDatabaseError do begin raise EOleDbException.CreateFmt('Erreur lors de la cration du champ %s : %s', [columnName, e.Message]); end; end; // On peut passer la colonne suivante : integer(Column) := integer(Column) + sizeof(DBCOLUMNINFO); end; finally FieldDefs.EndUpdate; end; CalcRecordSize; // calcule le buffer de stockage des lignes finally Cnt.FMAlloc.Free(ColumnsInfo); Cnt.FMAlloc.Free(Infos); end; end;
Le code ci-dessus tient compte de quelques particularits : Pour crer les TFieldDef, tous les champs doivent avoir un nom et on ne doit pas avoir deux champs avec le mme nom. Or si la requte d'origine retourne des champs calculs sans les avoir nomms, SQL Server va nous renvoyer un jeu de rsultats avec des champs sans noms. De mme si on fait une jointure sur deux
- 20 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
tables et qu'on retourne des champs qui portent le mme nom, on obtiendra des champs de mme nom dans le jeu de rsultats. Aussi, Describe nomme automatiquement les champs sans nom et les renomme en cas de doublons. Avec SQL Server, les champs dont la longueur n'est pas limite (varchar(max), varbinary(max)) remontent comme tant des champs de longueur -1 (ou $FFFFFFFF si on est en non sign). Describe teste ce cas particulier pour en faire des ftMemo. Lorsqu'on a fini de dfinir les TFieldDef, la classe TMemoryDataSet a besoin qu'on appelle CalcRecordSize avant qu'on puisse intervenir directement sur les buffers internes. Cette mthode est appele automatiquement l'ouverture du dataset. Cependant par scurit, il vaut mieux l'appeler ds qu'on a finit de modifier FieldDefs. Enfin, lorsqu'on a finit de travailler avec ColumnsInfo, il faut librer la mmoire qui a t alloue par OLEDB. Cette dernire a t alloue avec le gestionnaire de mmoire COM. Il faut la librer de la mme faon avec IMAlloc.Free.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
paramtres (DBACCESSOR_PARAMETERDATA). Ces derniers servent lire/crire les valeurs des paramtres pour les requtes paramtres. Le chargement des donnes doit ainsi dbuter par la cration d'un accesseur :
OleDbCheck((RowSet as IAccessor).CreateAccessor( DBACCESSOR_ROWDATA + DBACCESSOR_OPTIMIZED, FieldDefs.Count, @Bindings[0], RecordSize, Accessor, nil));
Bindings est un tableau de structures DBBINDING dfinissant le binding utiliser pour chaque champ. Il faut le renseigner avant de crer l'accesseur. Pour cela, on dfinit la mthode InitializeBindings. Cette dernire va initialiser le tableau en fonction des champs dfinis dans les FieldDefs, et donc de l'organisation des buffers de stockage l'intrieur de la classe TMemoryDataset :
procedure TCustomOleDbDataSet.InitializeBindings; var i : integer; FieldDef : TFieldDef; Binding : PDBBinding; begin SetLength(Bindings, FieldDefs.Count); for i := 0 to FieldDefs.Count-1 do begin FieldDef := FieldDefs[i]; Binding := @Bindings[i]; Binding.iOrdinal := i+1; Binding.pTypeInfo := nil; Binding.obValue := FFieldInfo[i].Offset+8; // Offset de la parti donnes du champ Binding.obLength := FFieldInfo[i].Offset+4; // Offset de la parti longueur du champ Binding.obStatus := FFieldInfo[i].Offset; // offset de la parti status du champ Binding.cbMaxLen := FFieldInfo[i].Size; // taille de la zone donnes en octets. Binding.dwPart := DBPART_VALUE or DBPART_LENGTH or DBPART_STATUS; // Les trois champs sont dfinis Binding.pObject := nil; Binding.pBindExt := nil; Binding.dwFlags := 0; Binding.eParamIO := DBPARAMIO_NOTPARAM; // Il ne s'agit pas d'un paramtre Binding.dwMemOwner:= DBMEMOWNER_CLIENTOWNED; // Le client est propritaire de la mmoire. Binding.bPrecision:= 0; Binding.bScale := 0; Binding.wType := DBTYPE_STR; case FieldDef.DataType of ftString, ftFixedChar: Binding.wType := DBTYPE_STR; ftSmallint : Binding.wType := DBTYPE_I2; ftWord: Binding.wType := DBTYPE_UI2; ftAutoInc, ftInteger: Binding.wType := DBTYPE_I4; ftBoolean: Binding.wType := DBTYPE_BOOL; ftFloat: Binding.wType := DBTYPE_R8; ftCurrency: Binding.wType := DBTYPE_R8; ftBCD: Binding.wType := DBTYPE_CY; ftDate, ftTime, ftDateTime : Binding.wType := DBTYPE_DATE; ftTimeStamp: // Chane de caractres // Entier court sign // Entier court non sign // Entier sign // Boolen // Nombre flottant sur 64 bits // Nombre flottant sur 64 bits // Nombre dcimal en virgule fixe (currency) // Date de type TDateTime // Date de type TTimeStamp
- 22 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
Binding.wType := DBTYPE_DBTIMESTAMP; ftLargeint: // entier long 64 bits Binding.wType := DBTYPE_I8; ftWideString, ftFixedWideChar: // Chane de caractres UNICODE Binding.wType := DBTYPE_WSTR; ftMemo: // Le champ est un pointeur sur une chaine de caractres. Binding.wType := DBTYPE_STR + DBTYPE_BYREF; ftBlob: // Le champ est un pointeur sur un tableau binaire Binding.wType := DBTYPE_BYTES + DBTYPE_BYREF; ftWideMemo: // Le champ est un pointeur sur une chaine de caractres unicode. Binding.wType := DBTYPE_WSTR + DBTYPE_BYREF; ftGUID: // Les GUID sont stocks en chane de caractres Binding.wType := DBTYPE_STR; else raise EOleDbException.Createfmt('Le type du champ %s n''est pas support !', [FieldDef.Name]); end; end; end;
- 23 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
Les structures DBBINDING sont renseignes de la faon suivante : Champ iOrdinal obValue obLength obStatus cbMaxLen dwPart eParamIO Valeur Indique le numro de la colonne ( partir de 1). On le dfinit simplement avec l'index du champ. Cette zone indique o placer la valeur du champ l'intrieur du buffer de destination. On doit indiquer l'offset de la zone de donne par rapport au dbut du buffer. Cette zone indique o mmoriser la longueur effective du champ pour les champs de longueur variable (varchar, nvarchar...). Cette zone indique o mmoriser le status du champ. Il s'agit principalement d'indiquer si le champ possde une valeur ou s'il vaut NULL dans la base de donnes. Cette zone indique la longueur maximale disponible l'intrieur du buffer pour stocker les donnes du champ. Si la taille relle du champ dpasse cette valeur, les donnes seront tronques. Il s'agit d'un masque qu'on doit renseigner pour indiquer si les attributs obValue, obLength et obStatus ont t dfinis dans la structure et donc si leur valeur est valide. Ce champ sert pour les accesseurs dfinissant des paramtres. Il sert indiquer s'il s'agit d'un paramtre d'entre ou de sorti. Pour un accesseur de donnes, on dfinit la valeur DBPARAMIO_NOTPARAM Ce champ est important lorsqu'on veut lire les donnes d'un champ dfini par rfrence (Cf wType). Il permet d'indiquer qui est propritaire de la zone mmoire pointe par la rfrence. Avec DBMEMOWNER_CLIENTOWNED, c'est le client qui possde la mmoire et qui sera responsable de sa libration. Pour les champs qui ne sont pas dfinis par rfrence, dwMemOwner doit obligatoirement tre dfinit DBMEMOWNER_CLIENTOWNED. Ce champ est trs important, c'est lui qui dfinit le type de donnes du champ. Il dfinit ainsi le format de stockage dans le buffer. Si on dfinit un type diffrent du type rel du champ, OLEDB se chargera d'effectuer les conversions avant d'alimenter le buffer. Ainsi pour les dates, on peut demander le type DBTYPE_DATE et laisser le provider se dbrouiller pour nous fournir un TDateTime. Il est possible de spcifier l'option DBTYPE_BYREF dans le type de donnes. Ca signifie que le champ ne contient pas directement la valeur, mais un pointeur sur cette valeur.
dwMemOwner
wType
- 24 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
En fait, les structures DBBINDING sont quivalentes la classe TFieldDef. Elles dfinissent l'organisation des donnes l'intrieur des buffers des lignes du dataset final. Pour les champs LOB (les champs dont la longueur maximale n'est pas limite comme les memos, binaires...), on est confront au fait qu'on ne peut pas connatre priori la longueur du champ. On ne peut donc pas dfinir un buffer de taille suffisante pour charger la totalit du LOB. Avec OLEDB, on peut rsoudre le problme trs facilement grce aux champs dfinis par rfrence. Lors de la dfinition du type du binding, on positionne le flag DBTYPE_BYREF. Avec ce dernier, OLEDB ne remplira pas directement notre buffer avec les donnes du champ, mais dfinira un pointeur sur un autre buffer qui lui contiendra rellement les donnes voulues. Le buffer est allou et rempli par le provider OLEDB. Il faudra simplement qu'on recopie cette valeur dans le dataset.
GetNextRows retourne dans RowHandle un tableau contenant un handle pour chaque ligne qui a t lue. On peut ensuite utiliser ces handles pour obtenir les donnes des lignes. On remarquera au passage qu'aprs GetNextRows, on peut accder directement n'importe quelle ligne partir de son handle. Il n'est pas ncessaire de respecter l'ordre squentiel.
- 25 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
Lorsqu'on dispose du handle d'une ligne, on peut lire ses donnes grce l'accesseur dfini prcdemment :
Cnt.OleDbCheck(RowSet.GetData(RowHandle[i], Accessor, Data));
Data est un pointeur sur le buffer destin recevoir les donnes de la ligne. C'est--dire le buffer de stockage d'une ligne du dataset. Accessor dsigne l'accesseur qui dfinit l'organisation du buffer Data.
On peut appeler GetData autant de fois qu'on veut pour une mme ligne. Dans l'implmentation de TOleDbDataset, on a choisit de dfinir un seul accesseur qui regroupe tous les champs de la ligne. Cependant on aurait aussi pu dfinir un accesseur par champ et charger les champs un par un en appelant GetData autant de fois que ce qu'on a dfinit d'accesseurs (c'est ce que semble faire ADO). Lorsqu'on a fini de travailler avec les donnes d'une ligne, on doit demander au provider de dtruire les handles des lignes qu'il a crs. C'est ce qu'on fait avec ReleaseRows :
RowSet.ReleaseRows(nbRow, RowHandle, nil, nil, nil);
Cette tape permet galement au provider de librer la mmoire alloue pour stocker les lignes. Il ne reste plus qu' runir le tout pour crire la mthode FetchAll :
// Charge les donnes retourne par IRowSet l'intrieur du dataset ds. procedure TOleDbDataSet.FetchAll(RowSet: IRowSet; Cnt : TOleDbConnection); var Accessor : HACCESSOR; nbRow : cardinal; RowHandle : PUintArray; FetchResult : hResult; i,j : integer; FPosition : cardinal; Data : Pointer; FIeldData : PFieldData; FieldOffset : cardinal; ptrBlob : pointer; begin // Premirement, il faut configurer le binding par rapport la structure des lignes du dataset. InitializeBindings; // Ensuite, on cre un accesseur bas sur ce binding. Cnt.OleDbCheck((RowSet as IAccessor).CreateAccessor( DBACCESSOR_ROWDATA + DBACCESSOR_OPTIMIZED, FieldDefs.Count, @Bindings[0], RecordSize, Accessor, nil)); try // A prsent, il ne reste plus qu' lire les lignes. nbRow := 0; // Les lignes vont tre lues par blocs de FPageSize lignes. // Il faut commencer par allouer un tableau qui contiendra les handles de ces lignes. GetMem(RowHandle, FPageSize*sizeof(integer)); try repeat // Dans un premier temps, il faut demander au provider de rcuprer les lignes depuis le serveur. // Elles restent stockes dans des buffers internes au provider. FetchResult := RowSet.GetNextRows(DB_NULL_HCHAPTER, 0, // On lit les lignes depuis la dernire lecture FPageSize, // On lit autant de ligne que dans une page du dataset. nbRow, // Nombre de lignes rellement lues. RowHandle);
- 26 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
Cnt.OleDbCheck(FetchResult); // Les lignes ont t lues on peut demander au provider d'alimenter notre dataset avec les // rsultats de cette lecture. for i := 0 to nbRow-1 do begin // On calcul le pointeur sur la ligne, dans les pages de stockage de TMemoryDataSet. Data := GetNewline(FPosition); // Ensuite on demande OLEDB de remplir la ligne pointe avec les donnes, en respectant // le binding dfinit dans l'accesseur. Cnt.OleDbCheck(RowSet.GetData(RowHandle[i], Accessor, Data)); // Une fois les donnes retournes par le provider, il reste convertir // les LOB pour les stocker dans des TMemoryBlobStream. for j := 0 to FBlobFields.Count -1 do begin // On commence par obtenir le pointeur sur les donnes du champ FieldOffset := FFieldInfo[cardinal(FBlobFields[j])-1].Offset; FieldData := GetPFieldData(Data, FieldOffset); ptrBlob := pointer(FieldData^.Data); // On recopie les donnes du Blob l'intrieur du DataSet. LoadBlobField(Data, cardinal(FBlobFields[j]), ptrBlob); // Enfin il ne faut pas oublier de librer la mmoire alloue par le provider // OLEDB pour stocker le BLOB. Cette mmoire n'a t alloue que si le champ // ne vaut pas NULL. if FieldData.NullStatus = DBSTATUS_S_OK then cnt.FMAlloc.Free(ptrBlob); end; // Enfin, on dfinit le bookmark de la ligne. DefineBookmark(FPosition, Data); end; // La page vient d'tre traite, on peut dire au provider de librer les lignes. RowSet.ReleaseRows(nbRow, RowHandle, nil, nil, nil); until FetchResult = DB_S_ENDOFROWSET; finally Freemem(RowHandle); end; finally // Il reste librer l'accesseur. Cnt.OleDbCheck((RowSet as IAccessor).ReleaseAccessor(Accessor, nil)); end; end;
Comme on peut le constater, les donnes des lignes sont directement charges l'intrieur des buffers de la classe TMemoryDataSet. On appelle GetNewLine pour ajouter une ligne vide dans TMemoryDataSet et obtenir le pointeur sur le buffer correspondant. Ensuite les donnes de la ligne sont charges avec GetData. Cependant, on doit quand mme effectuer une conversion sur les champs LOB. En effet ces derniers ne sont pas stocks correctement puisque le champ du LOB l'intrieur du buffer de la ligne contient un pointeur sur les donnes du LOB au lieu d'un numro de LOB. Cette conversion est faite en appelant LoadBlobField. Une fois le LOB converti, on peut librer la mmoire qui a t alloue par OLEDB en appelant IMAlloc.Free. Il ne reste plus qu' dfinir son bookmark avec DefineBookmark.
- 27 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
http://fsoriano.developpez.com/articles/db/oledb/delphi/
Binding.bPrecision:= 0; Binding.bScale := 0; Binding.wType := DBTYPE_STR; // wType sera rdfinit ensuite en fonction du paramtre. // On dfinit eParamIO en fonction du type dfinit pour le paramtre case Param.ParamType of ptUnknown, ptInput: Binding.eParamIO := DBPARAMIO_INPUT; ptOutput : Binding.eParamIO := DBPARAMIO_OUTPUT; ptInputOutput : Binding.eParamIO := DBPARAMIO_INPUT or DBPARAMIO_OUTPUT; end; // Premirement on regarde si le paramtre est NULL. Si c'est le cas, il // faut indiquer DBSTATUS_S_ISNULL pour le status du champ. if Param.IsNull then PData^.NullStatus := DBSTATUS_S_ISNULL else PData^.NullStatus := DBSTATUS_S_OK; // Maintenant, on fait la copie de la valeur du paramtre dans FData // fonction du type de ce dernier. case Param.DataType of ftWord, ftBoolean: begin ParamSize := 2; CheckSize(Offset + 8 + ParamSize); value := Param.AsInteger; move(value, PData^.Data[0], ParamSize); Binding.wType := DBTYPE_UI2; end; ftSmallint: begin ParamSize := 2; CheckSize(Offset + 8 + ParamSize); value := Param.AsInteger; move(value, PData^.Data[0], ParamSize); Binding.wType := DBTYPE_I2; end; ftAutoInc, ftInteger, ftLargeint: begin ParamSize := 4; CheckSize(Offset + 8 + ParamSize); value := Param.AsInteger; move(value, PData^.Data[0], ParamSize); Binding.wType := DBTYPE_I4; end; ftDateTime: begin ParamSize := 8; CheckSize(Offset + 8 + ParamSize); DateValue := Param.AsDateTime; move(DateValue, PData^.Data[0], ParamSize); Binding.wType := DBTYPE_DATE; end; ftDate, ftTime: begin ParamSize := 8; CheckSize(Offset + 8 + ParamSize); DateValue := Param.AsDate; move(DateValue, PData^.Data[0], ParamSize); Binding.wType := DBTYPE_DATE; end; ftFloat, ftCurrency, ftBCD : begin CheckSize(Offset + 8 + ParamSize); FloatValue := Param.AsFloat; move(FloatValue, PData^.Data[0], ParamSize); Binding.wType := DBTYPE_R8; end; ftBlob, ftGraphic, ftFmtMemo, ftBytes, ftVarBytes, ftString, ftMemo: begin en
- 29 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
stringValue := Param.AsString; ParamSize := length(stringValue); Binding.cbMaxLen := ParamSize; CheckSize(Offset + 8 + ParamSize); PData^.LengthValue := ParamSize; move(stringValue[1], PData^.Data[0], ParamSize); if (Param.DataType = ftMemo) or (Param.DataType = ftString) then Binding.wType := DBTYPE_STR else Binding.wType := DBTYPE_BYTES; end; ftWideString, ftWideMemo: begin widestringValue := Param.AsWideString; ParamSize := length(widestringValue)*2; Binding.cbMaxLen := ParamSize; CheckSize(Offset + 8 + ParamSize); PData^.LengthValue := length(widestringValue); move(widestringValue[1], PData^.Data[0], ParamSize); if (Param.DataType = ftMemo) or (Param.DataType = ftString) then Binding.wType := DBTYPE_STR else Binding.wType := DBTYPE_BYTES; end; end; inc(offset, ParamSize + 8); end; // A prsent, il ne reste plus qu' dfinir l'accesseur. Cmd := ACmd; Cnt.OleDbCheck((Cmd as IAccessor).CreateAccessor(DBACCESSOR_PARAMETERDATA, Params.Count, @Bindings[0], 0, hAccessor, nil)); Parameters.cParamSets := 1; Parameters.HACCESSOR := hAccessor; Parameters.pData := @FData[0]; end else begin Parameters.cParamSets := 0; Parameters.HACCESSOR := 0; Parameters.pData := nil; end; end;
- 30 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
Le constructeur lit la collection TParams en entre et initialise la structure Parameters qui sera utilise pour excuter la requte. Une fois la requte excute, la valeur des paramtres de sorti sera automatiquement mise en jour l'intrieur du buffer de TOleDbParams, grce l'accesseur. Il suffira donc de dcoder le buffer pour mettre jour la collection TParams avec les nouvelles valeurs :
procedure TOleDbParams.UpdateParams(Params: TParams); var i : integer; Param : TParam; PData : PFieldData; value : cardinal; DateValue : TDatetime; FloatValue : double; StringValue : string; WidestringValue : widestring; begin if Assigned(Params) and (Params.Count>0) then begin // On parcourt la liste des paramtres pour mettre jour la valeur de paramtres de sorti // en fonction du nouvel tat du buffer. for i := 0 to Params.Count -1 do begin Param := Params[i]; // On ne traite que les paramtres de sorti. Inutile de s'occuper de ceux qui sont en // entre seule. if (Param.ParamType in [ptOutput, ptInputOutput]) and (i<= high(FParamInfo)) then begin PData := @FData[FParamInfo[i].Offset]; if PData.NullStatus = DBSTATUS_S_ISNULL // Cas ou le paramtre vaut NULL then Param.Clear else begin // Si le paramtre ne vaut pas null, il reste copier la valeur du buffer // en fonction du type de donnes case Param.DataType of ftWord, ftBoolean, ftSmallint: // Entier sur 2 octets begin value := 0; move(PData.Data[0], value, 2); Param.AsInteger := value; end; ftAutoInc, ftInteger, ftLargeint: // Entier sur 4 octets begin move(PData.Data[0], value, 4); Param.AsInteger := value; end; ftDate, ftTime, ftDateTime: // Date begin move(PData^.Data[0], DateValue, 8); Param.AsDateTime := DateValue; end; ftFloat, ftCurrency, ftBCD : begin move(PData^.Data[0], FloatValue, 8); Param.AsFloat := FloatValue; end; ftBlob, ftGraphic, ftFmtMemo, ftBytes, ftVarBytes, ftString, ftMemo: begin SetLength(stringValue, PData.LengthValue); move(PData.Data[0], stringValue[1], PData.LengthValue); Param.AsString := stringValue; end; ftWideString, ftWideMemo: begin SetLength(WidestringValue, PData.LengthValue div 2);
- 31 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
move(PData.Data[0], WidestringValue[1], PData.LengthValue); Param.AsString := WidestringValue; end; end; end; end; end; end; end;
Attention : Selon le provider OLEDB utilis, le buffer ne sera mis jour avec les paramtres de sorti qu'une fois l'excution de la commande compltement termine, et que le jeux de rsultats aura t compltement lu (le Rowset a t libr). C'est notamment le cas avec SQL Server. Ca vient tout simplement de l'implmentation du protocole rseau utilis pour envoyer les donnes au client : Le provider OLEDB dcode le flux rseau au fur et mesure que les donnes sont lues. Or dans le flux TDS, les valeurs des paramtres de sorti sont tous simplement transmises aprs les donnes des SELECT. De sorte qu'on ne peut pas connaitre la valeur de ces paramtres tant qu'on n'a pas finit de lire toutes les donnes renvoyes par le select. Cette approche est trs basique. Les paramtres sont uniquement typs dans TParams partir de leur valeur initiale. Pour un paramtre de sorti, a signifie qu'il faut d'abord initialiser une valeur au paramtre pour pouvoir dimensionner le buffer intermdiaire. Cependant, la technique prsente ici est suffisante pour ce tutoriel sur OleDb. Au final, l'excution de la requte paramtre s'effectue de la faon suivante :
// Si la requte est paramtre, il faut initialiser les valeurs des paramtres. C'est le rle // de la classe TOleDbParams. Si elle n'attend pas de paramtre, TOleDbParams dfinit un jeu // de paramtres vide. OleDbParams := TOleDbParams.Create(Params, Cmd, self); try OleDbCheck(cmd.Execute(nil, IID_IRowset, OleDbParams.Parameters, nil, @unknown)); if Assigned(unknown) then begin RowSet := unknown as IRowSet; unknown := nil; // La source de donnes vient de rendre la main. On peut lire les donnes. ds := TOleDbDataSet(CreateDataSet); // On instancie le DataSet qui sera retourn. ds.LoadFromRowSet(RowSet, self, FetchSize); // Lecture des donnes. RowSet := nil; end else ds := nil; // Aprs l'excution de la requte on met jour les valeurs des paramtres de sorti. OleDbParams.UpdateParams(Params); finally OleDbParams.Free; end;
- 32 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
- 33 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
On remarquera qu'on prcise le niveau d'isolation de la transaction pour chaque transaction. Nous allons encapsuler cet appel dans la classe TCustomOleDbConnection pour fournir une mthode publique StartTransaction :
procedure TCustomOleDbConnection.StartTransaction; begin CheckConnected; // FTransaction vaut nil si le provider OLEDB ne gre pas les transactions if Assigned(FTransaction) then begin try // On trace le dbut de la transaction. SQLLogger.TraceStartTransaction(TransactionTime); // On appelle la mthode StartTransaction de l'interface OLEDB OleDbCheck(FTransaction.StartTransaction(ISOLATIONLEVEL_READCOMMITTED, 0, nil, nil)); except on e:exception do begin SQLLogger.TraceException(e); raise; end; end; end; end;
- 34 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
Lors de l'appel du commit, il faut prciser si on veut conserver le niveau d'isolation en cours ou revenir au niveau par dfaut configur pour la connexion. Le deuxime paramtre sert grer le commit deux phases pour les transactions distribues ainsi que le commit asynchrone. Ce n'est pas l'objet de ce tutoriel, on se contentera d'utiliser la valeur XACTIC_SYNC pour faire un commit standard. Le dernier paramtre est une valeur rserve qui doit toujours valoir 0. Il ne reste plus qu' ajouter une mthode Commit dans TCustomOleDbConnection :
procedure TCustomOleDbConnection.Commit; begin CheckConnected; if Assigned(FTransaction) then begin try SQLLogger.TraceCommitTransaction(TransactionTime); OleDbCheck(FTransaction.Commit(false, XACTTC_SYNC, 0)); except on e:exception do begin SQLLogger.TraceException(e); raise; end; end; end; end;
- 35 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
- 36 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
III-B - Connexion/Dconnexion
Il suffit de renseigner la chane de connexion et de dfinir la proprit Connected True :
var cnt : TOleDbConnection; begin // Cration d'un nouvel objet TOleDbConnection cnt := TOleDbConnection.Create(nil); // Ici on demande la construction d'une nouvelle chane de connexion. cnt.ConnectionString := TOleDbConnection.PromptConnexionString('', Application.MainFormHandle); // Enfin, on ouvre la connexion : cnt.Connected := true; // Maintenant on ferme la connexion. cnt.Connected := false;
- 37 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
On peut constater que les lignes vertes LOGIN et LOGOUT indiquent immdiatement que la connexion a t ouverte et ferme. La colonne Delta indique le temps coul en millisecondes depuis la ligne prcdente dans la trace. Comme chaque vnement a t prcd d'un message d'information indiquant la chane de connexion concerne, on peut en dduire le temps d'ouverture de la connexion (moins de 4 ms) et le temps de dconnexion (moins de 1 ms). En cas d'erreur au moment de la connexion la base, l'exception est galement automatiquement enregistre dans la trace :
- 38 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
Ici, on commence par ouvrir la connexion la base, puis on lit la table SalesLT.Customer dans sa totalit. OpenSQL renvoit un dataset en mmoire totalement dconnect de la base de donnes. Ca signifie qu'on peut faire ce qu'on veut du dataset retourn : On peut garder autant de dataset ouvert qu'on le souhaite et on peut mme fermer la connexion la base. Le dataset reste toujours valide. Il peut mme servir de table temporaire en mmoire. De plus, contrairement dbExpress, le dataset retourn est bidirectionnel. Ca signifie qu'on peut l'afficher dans une grille. Voyons prsent la trace gnre l'excution :
L'excution de la requte a gnr cinq lignes dans la trace : La premire ligne correspond au dbut de la requte. On voit la requte qui est envoye au SGBD. Lorsque le SGBD a fini de traiter la requte, il rend la main et on commence lire les rsultats. Cet instant est galement trac avec l'vnement Debut Fetch . Le Fetch en lui-mme prend un certain temps. Lorsqu'il est termin, un nouvel vnement indique la fin de la lecture des donnes : Fin Fetch . Enfin, l'excution de la requte est termine. Un nouvel vnement dans la trace indique la fin de OpenSQL, en rappelant la requte excute et le nombre de lignes lues.
Examinons maintenant les temps d'excution. Comme on peut le voir, l'excution de la requte sur le SGBD a dure moins de 1 ms. Par contre ensuite, la lecture des rsultats de la requte a pris environ 4 ms pour lire 440 lignes. De plus, on voit que chaque ligne lue occupe 1598 octets dans le dataset. Cet exemple montre clairement que de nos jours, l'excution d'une requte simple sur un SGBD est trs rapide. Par contre on passe ensuite l'essentiel du temps de traitement lire les rsultats de la requte. Or la dure du fetch est troitement lie l'API utilise. D'o l'importance de choisir une API performante.
- 39 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
Si nous n'avions pas travailler en local, les temps de fetch aurraient t encore plus importants.
Il suffit alors de lire la proprit InfoMessages pour lire le message renvoy. Cette proprit est rinitialise chaque excution d'une commande SQL.
- 40 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/
IV - Conclusion
Dans cet article, nous avons vu les bases de OLEDB. Nous avons vu comment utiliser cette API bas niveau afin d'excuter des requtes SQL sur une base de donnes. De premier abord, OLEDB est loin d'tre simple. Mais si on s'y intresse de plus prs, il suffit de comprendre le fonctionnement du binding et des accesseurs. Aprs, le reste n'est pas plus compliqu qu'utiliser ADO. Lorsqu'on utilise OLEDB directement, on peut obtenir des performances vraiment excelentes. Par exemple, on a pu excuter une requte renvoyant tous les clients de la base AdventureWorksLT (440 lignes en tout) en 4 ms. Dans le prochain article nous verrons comment OLEDB et SQL Server nous permettent d'effectuer des chargements de donnes en blocs. On pourrait ainsi insrer massivement des donnes dans une table, directement depuis une application Delphi, la vitesse d'un bcp, DTS, ou autre SSIS.
V - Rfrences
Le Tracing avec Event Tracing for Windows (ETW) : http://fsoriano.developpez.com/articles/etw/delphi/ Comparatif des architectures des API d'accs aux donnes : http://fsoriano.developpez.com/articles/db/comparatifapi/ Dvelopper un DataSet en mmoire : http://fsoriano.developpez.com/articles/db/dataset/delphi La documentation OLEDB sur MSDN : http://msdn.microsoft.com/en-us/library/ms722784(VS.85).aspx La base de donnes d'exemple AdventureWorksLT : http://www.codeplex.com/MSFTDBProdSamples/Release/ProjectReleases.aspx?ReleaseId=4004
VI - Remerciements
Je remercie particulirement Nono40 pour sa relecture et conseils aviss !
- 41 Les sources prsentes sur cette page sont libres de droits et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteur. Copyright 2009 Franck SORIANO. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' trois ans de prison et jusqu' 300 000 de dommages et intrts. Cette page est dpose la SACD.
http://fsoriano.developpez.com/articles/db/oledb/delphi/