Vous êtes sur la page 1sur 124

Interbase Borland Data Provider - John COLIBRI.

abstract:utilisation du Borland Data Provider


sous Windows Forms pour grer en Delphi 8 des donnes Interbase
mots cl:BDP - Borland Data Provider - .Net SDK Windows Forms - Delphi 8 - Interbase - Firebird
logiciel utilis: Windows XP, Delphi 8, Interbase 6
matriel utilis: Pentium 1.400Mhz, 256 M de mmoire
champ d'application: Delphi 8 sur Windows, Interbase, Firebird
niveau: dbutant Delphi et Base de Donnes
plan:
o Introduction
o Architecture
o Connexion la base
o Crer une Table
o Ajouter des Donnes
o Lire et Afficher
o Modifier des Donnes
o Evaluation
o Tlcharger les Exemples
o Conclusion

1 - Introduction
J'ai choisi de prsenter Borcon 2004 les diffrents moyens d'utiliser Interbase:

pour Delphi 5 6, les API, le BDE


et Sql Links, Interbase Express, dbExpress et Ado
pour Delphi 8:
o en mode VCL.Net, dbExpress.Net, Ibx.Net
o en mode Windows Forms, Ado.Net et le BDP

Cet article va se concentrer sur le mode Windows Forms et


le Borland Data Provider.
Pour les autres faons d'utiliser Interbase, vous pouvez consulter sur ce site:

le tutorial interbase: comment utiliser des bases de donnes en


mode Client Serveur (Delphi 6, Ibx). L'article d'initiation le plus complet
Interbase dbExpresss: le mode dbExpress (Delphi 6, dbExpress). Le mode
qui permet le mieux de comprendre l'architecture Ado.Net qui en est
directement issue

Interbase Ibx.Net: le portage sous .Net de la


mcanique Ibx (Delphi 8, Ibx.Net). Le moyen le plus simple
d'utiliser Interbase et Delphi 8. Contient aussi un comparatif de toutes ces
architectures avec les schmas correspondants
Interbase dbExpress.Net: le portage sous .Net de la
mcanique dbExpress (Delphi 8, dbExpress.Net). L'utilisation des
techniques VCL pour Interbase ET pour les autres serveurs
(Oracle, Sql Server, MyBase etc)

Nous nous adressons pour cet article un programmeur Delphi ayant une ide
lmentaire de Delphi: Palette, Inspecteur, OnClick. Tout le reste sera expliqu.
Nous supposons:

que Delphi 8 est install (la version "dveloppeur": pas la version "tudiant"
qui n'a pas les bases de donnes, et pas besoin de la version "Entreprise" ou
"Architecte", mais tout marche aussi avec ces deux dernires)
qu'Interbase est install (voyez le tutorial interbase qui dtaille cette
installation). J'ai utilis la version Interbase 6 que j'ai installe et que j'utilise
couremment avecDelphi 6, mais tout fonctionne bien sr avec les versions 7
et FireBird.

2 - Architecture
Une fois les librairies .Net installes, nous pouvons utiliser des Serveurs de bases
de donnes en utilisant une couche appele Ado.Net. Cette couche dfinit des
composant pour communiquer avec des "sources de donnes". Ces sources sont trs
gnrales: des courriers Outlook, des donnes Excel et des donnes provenant
d'un Serveur Sql.
Pour les bases de donnes, Ado.Net permet de grer:

de faon native
o "Sql Data Provider" pour Sql Server
en ajoutant des modules supplmentaires, d'autres bases de donnes:
o pour Oracle, le module "Oracle Data Provider"
o pour les serveurs pour lesquels existe un pilote ODBC, le module
"ODBC Data Provider"

o
o

pour les serveurs ayant un pilote OleDb COM, le module "OleDb


Data Provider"
le module "Borland Data Provider" qui est un provider gnrique pour
plusieurs serveurs, tels
que Sql Server, Oracle, DB2, FireBird et Interbase.

Pour accder Interbase, nous pouvons donc:

acheter un pilote ODBC (EasySoft)


acheter un pilote OleDb (IbProvider)
utiliser le BDP fourni avec Delphi 8

Notez aussi que le BDP est un module .Net gnral: il peut tre utilis avec
n'importe quel langage qui communique avec les librairies .Net (Delphi 8, bien sr,
mais aussi les autres langages via les divers outils de dveloppement).
L'architecture gnrale a donc l'allure suivante:

3 - La connection
3.1 - Prparation
Le BDP ne sait pas crer de base vierge. Seul Ibx, en mode Delphi 6, ou Ibx.Net en
mode Delphi 8 peuvent le faire. Nous allons donc utiliser une base cre
auparavant, en la copiant dans un rpertoire proche pour viter de la rechercher
niches dans des rpertoires dpendant de chaque installation. Nous allons:
crer un rpertoire _DATA au mme niveau que nos sources (dans n'importe quel
rpertoire qui vous convient)
copier une base Interbase dans ce rpertoire. Par exemple INSTITUT_PASCAL.GDB
provenant de nos tutoriaux antrieurs, ou EMPLOYEE.GDB provenant de "FICHIERS
COMMUNS" etc
renommer cette base INSTITUT_PASCAL_5.GDB

3.2 - La connection
Commenons par nous connecter note base Interbase:
lancez Delphi 8
crez une nouvelle application en cliquant "Fichiers | Nouveau | Windows Forms" et
renommez-la "p_ib_bdp_connection"
dans la page "Borland Data Provider" slectionnez un BdpConnection:

et posez-le sur le Forme


comme d'habitude, le BdpConnection est affich sous la Windows Forms:

slectionner BdpConnection1, et avec un Click droit souris, ouvrez


l'Editeur de Connections. Par dfaut il affiche les proprits du serveur DB2:

tapez les proprits de notre base:

slectionnez le pilote IbConn1


tapez le chemin
_DATA\INSTITUT_PASCAL_5.GDB

testez la connection en cliquant "Test"


Si le chemin et le nom de la base sont corrects, un dialogue "Connection
Successful" apparat. Fermez-le

fermez le dialogue de connection en cliquant "OK"

Notez que

j'ai eu le plaisir de voir que le "UserName" et "Password" sont dj remplis, et


que le LoginPrompt est False par dfaut. Passer sa journe, sur sa propre
machine, taper les sempiternels "SYSDBA" et "masterkey" n'est pas
particulirement productif.
le dialecte 3 est dj initialis (nous avons cr notre base avec ce dialecte).

Delphi a rempli les proprits ConnectionString et ConnectionOptions dans


l'Inspecteur d'Objet:

ConnectionString contient:
assembly=Borland.Data.Interbase,
Version=1.5.0.0,
Culture=neutral,
PublicKeyToken=91d62ebb5b0d1b1b;
vendorclient=gds32.dll;
database=..\_data\INSTITUT_PASCAL_5.GDB;
provider=Interbase;
username=sysdba;
password=masterkey

ConnectionOption contient:
waitonlocks=False;
commitretain=False;
sqldialect=3;
transaction isolation=ReadCommitted;
servercharset=;
rolename=myrole

4 - Crer et remplir une Table


4.1 - Principe
Nous allons crer une table contenant pour chaque formation:

un code (par exemple 8)


un nom (par exemple "Delphi Interbase")
le nombre de jours (par exemple 3)
un prix (par exemple 1.400)

La syntaxe de la requte SQL envoyer vers le Server est la suivante:


CREATE TABLE formations_5
(f_numero INTEGER, f_nom CHARACTER(23), f_jours INTEGER,
f_prix NUMERIC(5, 2) )

Pour envoyer cette requte vers le Serveur:

nous utilisons un BdpConnection qui assurera la connection vers le Serveur


nous crons un objet BdpCommand en utilisant:

my_c_bdp_command:= my_c_bdp_connection.CreateCommand;

nous remplissons la requte par:


my_c_bdp_command.CommandText:= 'CREATE etc';

nous envoyons la requte vers le Serveur par:


my_c_bdp_command.ExecuteNonQuery;

Nous pouvons aussi ajouter des transactions pour encadrer cette cration.

Donc:
crez une nouvelle application Windows Forms et appelez-la "p_ib_bdp_create_table"
posez un Button, appelez-le "create_", crez son vnement OnClick et tapez les
instructions de cration de table:
const k_database_file= '..\_data\INSTITUT_PASCAL_5.GDB';
k_table_name= 'formations_5';
k_connection_string= 'assembly=Borland.Data.Interbase, Version=1.5.0.0, '
+ 'Culture=neutral,PublicKeyToken=91d62ebb5b0d1b1b;vendorclient=gds32.dll
;'
+ 'database='+ k_database_file+ ';'
+ 'provider=Interbase;username=sysdba;password=masterkey';
k_sql_create= 'CREATE TABLE '+ k_table_name
+ ' (f_id INTEGER, f_name CHAR(28), f_days INTEGER, f_price DOUBLE P
RECISION )';
procedure TWinForm.create_transaction__Click(sender: System.Object; e: System.E
ventArgs);
var l_c_bdp_connection: BdpCOnnection;
l_c_bdp_transaction: BdpTransaction;
l_c_bdp_command: BdpCommand;
begin
try
try
l_c_bdp_connection:= BdpConnection.Create(k_connection_string);
Include(l_c_bdp_connection.StateChange, BdpConnection1_StateChange);
l_c_bdp_connection.Open;

l_c_bdp_transaction:= l_c_bdp_connection.BeginTransaction;
l_c_bdp_command:= l_c_bdp_connection.CreateCommand;
l_c_bdp_command.CommandText:= k_sql_create;
l_c_bdp_command.Connection:= l_c_bdp_connection;
l_c_bdp_command.Transaction:= l_c_bdp_transaction;
l_c_bdp_command.ExecuteNonQuery;
finally
l_c_bdp_transaction.Commit;
l_c_bdp_connection.Close;
end;
except
on e:exception do
begin
l_c_bdp_transaction.RollBack;
display_bug_stop(e.Message);
end;
end; // try ... except
end; // create_transaction__Click
compilez, excutez, et cliquez le bouton

Dans la foule, nous pouvons effacer la mme table avec la requte Sql suivante:
DROP TABLE formations_5

Donc:
placez un autre tButton sur la Forme, nommez-le "drop_table" et placez-y la requte de
suppression:
const k_sql_drop= 'DROP TABLE '+ k_table_name;
procedure TWinForm.drop_transaction__Click(sender: System.Object; e: System.Ev
entArgs);
var l_c_bdp_connection: BdpCOnnection;
l_c_bdp_transaction: BdpTransaction;
l_c_bdp_command: BdpCommand;
begin
try
try
l_c_bdp_connection:= BdpConnection.Create(k_connection_string);
Include(l_c_bdp_connection.StateChange, BdpConnection1_StateChange);

l_c_bdp_connection.Open;
l_c_bdp_transaction:= l_c_bdp_connection.BeginTransaction;
l_c_bdp_command:= l_c_bdp_connection.CreateCommand;
l_c_bdp_command.CommandText:= k_sql_drop;
l_c_bdp_command.Connection:= l_c_bdp_connection;
l_c_bdp_command.Transaction:= l_c_bdp_transaction;
l_c_bdp_command.ExecuteNonQuery;
finally
l_c_bdp_transaction.Commit;
l_c_bdp_connection.Close;
end;
except
on e:exception do
begin
l_c_bdp_transaction.RollBack;
display_bug_stop(e.Message);
end;
end; // try ... except
end; // drop_transaction__Click

compilez, excutez, et cliquez le bouton

Vous pouvez tlcharger le sources du projet "win_bdp_create_table.zip".

4.2 - Vrifier la cration


Nous pouvons vrifier que la cration a t effectue en utilisant la
fonction GetMetaData de BdpConnection. La technique est similaire celle utilise
partSession.GetTableNames du temps de l'antique BDE.
Dans notre cas:
crez une nouvelle application en cliquant "Fichiers | Nouveau | Windows Forms" et
renommez-la "p_win_bdp_metadata"
posez une ListBox sur la Forme
posez un Button, nommez-le "table_", crez son vnement OnClick qui va afficher

dans ListBox1 le nom des tables:


procedure TWinForm.tables_Click(sender: System.Object; e: System.EventArgs);
var l_c_bdp_connection: BdpCOnnection;
l_c_data_table: DataTable;
l_table_index: integer;
begin
try
Datagrid1.DataSource:= nil;
l_c_bdp_connection:= BdpConnection.Create(k_connection_string);
Include(l_c_bdp_connection.StateChange, BdpConnection1_StateChange);
l_c_bdp_connection.Open;
try
l_c_data_table:= l_c_bdp_connection.GetMetaData.GetTables('',
Borland.Data.Schema.TableType.Table);
DataGrid1.DataSource:= l_c_data_table;
ListBox2.Items.Clear;
for l_table_index:=0 to l_c_data_table.Rows.Count-1 do
ListBox2.Items.Add(l_c_data_table.Rows[l_table_index].Item['TableName']);
finally
l_c_bdp_connection.Close;
end;
except
on e:exception do
DataGrid1.CaptionText:= e.Message;
end; // try ... except
end; // tables_Click
posez un DataGrid sur la Forme
slectionnez ListBox1, crez son vnement MouseDown et affichez le contenu de la table
slectionne:
procedure TWinForm.ListBox2_MouseDown(sender: System.Object; e: System.Wind
ows.Forms.MouseEventArgs);
var l_c_bdp_connection: BdpCOnnection;
l_c_data_table: DataTable;
l_table_name: String;
begin
try
Datagrid1.DataSource:= nil;
Text:= k_database_file;
l_c_bdp_connection:= BdpConnection.Create(k_connection_string);
Include(l_c_bdp_connection.StateChange, BdpConnection1_StateChange);
l_c_bdp_connection.Open;

try
l_table_name:= ListBox2.Items[ListBox2.SelectedIndex].ToString;
l_c_data_table:= l_c_bdp_connection.GetMetaData.GetColumns(l_table_name,
'', Borland.Data.Schema.ColumnType.Unknown);
DataGrid1.DataSource:= l_c_data_table;
finally
l_c_bdp_connection.Close;
end;
except
on e:exception do
DataGrid1.CaptionText:=e.Message;
end;
end; // ListBox2_MouseDown
compilez et excutez. Cliquez "table_" et cliquez "FORMATIONS_5"
voici le rsultat:

5 - Ajouter des Donnes


Pour ajouter un enregistrement
3, Interbase Delphi
l'instruction SQL excuter est:

INSERT INTO formations


(f_numero, f_nom)
VALUES (3, 'Interbase Delphi')

Crons une nouvelle application pour pouvoir ajouter quelques lignes:


crez une nouvelle application Windows Forms et appelez-la "p_win_bdp_insert_data"
posez un Button, appelez-le "insert_", crez son vnement OnClick et tapez les
instructions qui insreront une ligne:
const k_sql_insert=
'INSERT INTO '+ k_table_name
+ ' (f_id, f_name, f_days, f_price)'
+ ' VALUES (3, ''Delphi Interbase'', 3, 1400)';
procedure TWinForm.insert_Click(sender: System.Object; e: System.EventArgs);
var l_c_bdp_connection: BdpCOnnection;
l_c_bdp_command: BdpCommand;
begin
try
try
l_c_bdp_connection:= BdpConnection.Create(k_connection_string);
Include(l_c_bdp_connection.StateChange, BdpConnection1_StateChange);
l_c_bdp_connection.Open;
l_c_bdp_command:= l_c_bdp_connection.CreateCommand;
l_c_bdp_command.CommandText:= k_sql_insert;
l_c_bdp_command.ExecuteNonQuery;
finally
l_c_bdp_connection.Close;
end;
except
on e:exception do
display_bug_stop(e.Message);
end; // try ... except
end; // insert_Click
compilez, excutez, et cliquez le bouton

Nous pouvons automatiser ces ajouts en paramtrant la procdure qui envoie les
valeurs littrales. Voici un exemple:
posez un Button, appelez-le "insert_several_", crez son vnement OnClick et tapez les

instructions qui insreront plusieurs lignes:


procedure TWinForm.insert_several__Click(sender: System.Object; e: System.Event
Args);
var l_c_bdp_connection: BdpCOnnection;
l_c_bdp_command: BdpCommand;
procedure insert_generic(p_number: Integer; p_name: String;
p_days: Integer; p_price: Double);
var l_sql_text: String;
begin
DecimalSeparator:= '.';
l_sql_text:=
'INSERT INTO '+ k_table_name
+ ' (f_id, f_name, f_days, f_price)'
+ ' VALUES '
+ ' ('
+
IntToStr(p_number)+ ', '+ QuotedStr(p_name)+ ', '
+
IntToStr(p_days)+ ', '+ FloatToStr(p_price)
+ ' )';
DecimalSeparator:= ',';
display(l_sql_text);
l_c_bdp_command.CommandText:= l_sql_text;
Try
l_c_bdp_command.ExecuteNonQuery;
Except
on e: Exception do
display(' *** pb_insert '+ e.Message);
end;
end; // insert_generic
begin // insert_several__Click
try
try
l_c_bdp_connection:= BdpConnection.Create(k_connection_string);
Include(l_c_bdp_connection.StateChange, BdpConnection1_StateChange);
l_c_bdp_connection.Open;
l_c_bdp_command:= l_c_bdp_connection.CreateCommand;
insert_generic(1, 'Initiation Delphi', 3, 1400.40);
insert_generic(2, 'Bases de Donnes Delphi', 3, 1400);
insert_generic(3, 'Interbase Delphi', 3, 1400);
insert_generic(4, 'Composants Delphi', 3, 1400);
insert_generic(5, 'UML et Patterns Delphi', 3, 1400);
insert_generic(6, 'Delphi Asp', 3, 1400);
finally
l_c_bdp_connection.Close;

end;
except
on e:exception do
display_bug_stop(e.Message);
end; // try ... except
end; // insert_several__Click
compilez, excutez, et cliquez le bouton

6 - Lire et Afficher
6.1 - Affichage par code
Nous allons maintenant afficher les donnes.
Une premire technique consiste lire les donnes en utilisant un DataReader.
Plaons cet affichage dans le projet prcdent:
posez un Button, appelez-le "select_", crez son vnement OnClick et tapez les
instructions qui afficheront les lignes dans un TextBox:
procedure TWinForm.select__Click(sender: System.Object; e: System.EventArgs);
var l_c_bdp_connection: BdpConnection;
l_c_bdp_command: BdpCommand;
l_c_data_reader: BdpDataReader;
l_field_index: Integer;
l_line: String;
l_formatted_value: String;
begin
try
try
l_c_bdp_connection:= BdpConnection.Create(k_connection_string);
Include(l_c_bdp_connection.StateChange, BdpConnection1_StateChange);
l_c_bdp_connection.Open;
l_c_bdp_command:= l_c_bdp_connection.CreateCommand;
l_c_bdp_command.CommandText:= k_sql_select;
l_c_data_reader:= l_c_bdp_command.ExecuteReader;
while l_c_data_reader.Read do
begin
l_line:= '';

for l_field_index:= 0 to l_c_data_reader.FieldCount- 1 do


begin
l_formatted_value:= l_c_data_reader.GetValue(l_field_index).ToString;
Case l_field_index of
0 : l_formatted_value:= Format('%-3s', [l_formatted_value]);
1 : l_formatted_value:= Format('%-28s', [l_formatted_value]);
2 : l_formatted_value:= Format('%-3s', [l_formatted_value]);
3 : l_formatted_value:= Format('%-6s', [l_formatted_value]);
end; // case
l_line:= l_line+ l_formatted_value+ '|'
end; // for all fields
display(l_line);
end; // while Read
Finally
l_c_data_reader.Close;
End; // try ... finally
Except
On e: Exception do
display('*** '+ e.Message)
end; // try ... except
display('< select');
end; // select__Click
compilez, excutez, et cliquez le bouton
voici le rsultat:

6.2 - Affichage en mode conception


Nous allons aussi utiliser des composants poss sur la Forme au lieu d'utiliser la
cration dynamique par code:
crez une nouvelle application Windows Forms et appelez-la "p_win_bdp_select"

connectez le Serveur:

dans la page "Borland Data Provider" slectionnez un BdpConnection et posez-le


sur le Forme
slectionner BdpConnection1, et avec un Click droit souris, ouvrez
l'Editeur de Connections
o slectionnez le pilote IbConn1
o tapez le chemin _DATA\INSTITUT_PASCAL_5.GDB
o testez la connection en cliquant "Test"
o fermez le dialogue de connection en cliquant "OK"

dans la page "Borland Data Provider" slectionnez un BdpDataAdapter:

et posez-le sur le Forme


Dfinissez la requte de slection:

slectionnez le BdpDataAdapter1, cliquez sur le bouton droit, slectionnez


"Configure Data Adapter"
le Configurateur de Data Adapter est affich:

slectionnez FORMATIONS_5, puis "Generate Sql"


le code "SELECT ... " est gnr

slectionnez l'onglet "Preview Data"


cliquez "Refresh"
les donnes de la table sont affichs:

pour afficher les donnes sur la Forme, nous crons un DataSet qui sera rempli
par le BdpDataprovider.
Pour cela, slectionnez l'onglet "Dataset" du configurateur, choisissez le bouton
"New Data":

Fermez le Configurateur en cliquant "OK"

DataSet1 est automatiquement ajout aux composants


De plus sur disque est apparu DataSet1Unit.pas (19 K, tout de mme) avec,
semble-t-il, les champs persistents et des vnements de gestion des
modifications et de traitements .XML

dans l'onglet "Data Controls" slectionnez un DataGrid:

et posez le sur la Forme.


Puis:

slectionnez sa proprit DataSource et mettez-y DataSet1


slectionnez sa proprit DataMember et mettez-y FORMATIONS_5

ouvrez le DataAdapter1: slectionnez sa proprit Active et basculez-la sur True


les donnes sont affiches dans DataGrid1:

7 - Modifier des Donnes


Nous allons prsent mettre en place la modification de donnes. Commenons par
une application qui affiche des donnes dans un DataGrid:
crez une nouvelle application Windows Forms et appelez-la "p_win_bdp_update_data"
connectez le Serveur:

dans la page "Borland Data Provider" slectionnez un BdpConnection et posez-le


sur le Forme
slectionner BdpConnection1, et avec un Click droit souris, ouvrez
l'Editeur de Connections
o slectionnez le pilote IbConn1
o tapez le chemin _DATA\INSTITUT_PASCAL_5.GDB
o testez la connection en cliquant "Test"

fermez le dialogue de connection en cliquant "OK"

dans la page "Borland Data Provider" slectionnez un BdpDataAdapter et posez-le sur


le Forme
Dfinissez la requte de slection:

slectionnez le BdpDataAdapter1, cliquez sur le bouton droit, slectionnez


"Configure Data Adapter"
slectionnez FORMATIONS_5, toutes ses colonnes, puis "Generate Sql"
le code "SELECT ... " est gnr
slectionnez l'onglet "Preview Data"
cliquez "Refresh"
les donnes de la table sont affichs
pour afficher les donnes sur la Forme, nous crons un DataSet qui sera rempli
par le BdpDataprovider.
Pour cela, slectionnez l'onglet "Dataset" du configurateur, choisissez le bouton
"New Data".
Fermez le Configurateur en cliquant "OK"

DataSet1 est automatiquement ajout aux composants et DataSet1Unit.pas est


gnr sur disque

dans l'onglet "Data Controls" slectionnez un DataGrid et posez le sur la Forme.


Puis:

slectionnez sa proprit DataSource et mettez-y DataSet1


slectionnez sa proprit DataMember et mettez-y FORMATIONS_5

ouvrez le DataAdapter1: slectionnez sa proprit Active et basculez-la sur True

A prsent la partie de mise jour:


slectionnez DataAdapter1 et sa proprit UpdateCommand: vous voyez dans sa
proprit CommandText la requte Sql suivante:
UPDATE FORMATIONS_5
SET F_ID = ?, F_NAME = ?, F_DAYS = ?, F_PRICE = ?
WHERE F_ID = ? AND F_NAME = ? AND F_DAYS = ?
AND F_PRICE = ?
Cette requte a en fait t gnre automatiquement lorsque nous avons cre la
requte SELECT.
Si cette requte ne nous convient pas, nous pourrions la modifier (changer

le WHERE pour retrouver l'ancienne ligne sur la cl uniquement, par exemple)


ajoutons l'instruction de mise jour: posez un Button sur la Forme, nommez-le "update_"
et crez son vnement OnClick:
const k_table_name= 'formations_5';
procedure TWinForm.update__Click(sender: System.Object; e: System.EventArgs);
begin
if DataSet1.HasChanges
then begin
BdpDataAdapter1.AutoUpdate(DataSet1, k_table_name, BdpUpdateMode.All,
[], []);
end;
end; // tables_Click
compilez, excutez. Puis

modifiez la ligne "Delphi Asp" en "ASP.NET ET DELPHI", par exemple


changez de ligne pour poster
cliquez "update_"

fermez et rouvrez: la modification a bien t enregistre

8 - Evaluation
Voici quelques commentaires sur cette premire utilisation du BDP

le fonctionnement est nettement plus sympathique qu'avec Ado.Net. Freud


dirait que j'aurais pu mettre ce commentaire avant de commencer utiliser
le BPD. Quel que soit mon biais,
l'empilement Ado.Net, Com Interoperability Layer, OleDb puis le
client Interbase (ou autre) ne peut pas tre trs efficace par dfaut
au niveau de l'interface programmeur:
o l'Editeur de Connection de BdpConnection ne se souvient pas toujours
de la connection courante (il s'ouvre toujours sur Db2Conn1)
pour les bugs:
o
NUMERIC :
j'avais au dbut utilis le type NUMERIC(5, 2) provenant d'un
prcdent article
pour une raison inconnue, l'affichage via un DataSet ne
fonctionnait pas. De plus toute la mcanique que nous avons
prsent pour afficher en mode conception n'arrtait pas de
prsenter des erreur ("input string not of the correct format")
j'ai chang le type rel en DOUBLE PRECISION et tout est
rentr dans l'ordre
o j'ai aussi eu des misres en essayant d'utiliser une table gre
en Delphi 6 et qui comportait des dates.
Par consquent:
il vaut mieux pour le moment s'en tenir aux types Interbase simples
o le diagnostic de l'erreur a t plus que laborieux. Heureusement que
j'avais display: l'affichage de l'indice du champ trait a dbusqu le
problme
BDP est proche dans sa philosophie de dbExpress, et s'en loigne par la mise
en oeuvre. Ce qui est trs proche:
o nous retrouvons la chane connection
(tSqlConnection / BdpConnection) adaptateur
(tDataSetProvider / BdpDataAdapter) dataset
(tClientDataset / DataSet) puis affichage (tdbGrid / DataGrid)
o la modification se fait en appelant Update et des paramtres qui
guident le mode de mise a jour
o

Et ce qui change:
avec le BDP, la relation entre l'adaptateur et le DataSet est initialise
par l'Adaptateur. C'est en fait toute l'architecture des composants de
visualisation qui a t gnralise, et ceci explique ce changement de
liaison
o le DataGrid a 2 proprits (DataSource ET DataMember)
d'une faon plus gnrale, la masse de code gnre par Delphi 8 pour faire
fonctionner l'ensemble est la fois impressionnant et, mon avis, un peu
o

inquitant: soit cette masse est imprative et c'est un dfaut, soit Borland a
forc le comportement trs gnral de Ado.Net pour le faonner au
moule Delphi 6 et cela m'incite voir quel serait le fonctionnement sans tout
cela...

Delphi 8 propose aussi un explorateur de donnes "Voir | Data Explorer":

et en cliquant "Interbase", "IbConn1" et FORMATIONS_5 nous avons bien notre


table et ses donnes:

Cet explorateur est en fait un .EXE autonome. Mais je n'ai pas trs bien su
l'exploiter:

comment fournir la base examiner (la chane de connection)


les changement de l'application Delphi semble slectionner ou modifier la
base utilise

Extraction de Script SQL - John COLIBRI.

rsum : Comment gnrer un script SQL partir d'une base de


donnes Interbase / Firebird
mots cl : script SQL - Interbase - Firebird - IbExtract
logiciel utilis : Windows XP personnel, Delphi 6.0
matriel utilis : Pentium 2.800 Mhz, 512 Meg de mmoire, 250
Giga disque dur
champ d'application : Delphi 1 2006, Turbo Delphi sur Windows
niveau : dveloppeur Delphi - dveloppeur Interbase
plan :
o Introduction
o Principe
o Le Projet Delphi
o Tlcharger le code source Delphi

1 - Introduction

Cet article va indiquer comment extraire d'une base de donnes existante un script
SQL capable de recrer la mme base.
Cet utilitaire permet:

de sauvegarder de faon lisible (mais verbeuse) les donnes d'une base


de porter facilement les donnes d'une version d'un mme moteur une autre
version
d'adapter et modifier les donnes pour les porter sur un autre moteur, ou
changer son fonctionnement (passer du dialecte Interbase 1 au dialecte 3)
de changer la structure de la base, ou la valeur des donnes
de documenter la base (en utilisant des outils de gnration de
documentation partir du script)

Nous avons utilis cet utilitaire dans notre article sur MastApp, la base de
dmonstration de Delphi

2 - Principe
Nous pouvons utiliser plusieurs techniques:

analyser les tables systme qui contiennent tout les informations sur la base
utiliser des composants Delphi qui sont capables d'extraire le schma
utiliser des outils externes tels que IbExpert

Comme Delphi fournit, depuis Delphi 6, un composant spcifique d'extraction de


schma pour Interbase, nous avons opt pour cette technique-l. Notez que si nous
souhaitions effectuer une extraction directe partir des tables systme, un moyen
commode serait d'examiner le code source d'Ibx et d'adapter ce code pour nos
propres besoins. Dans notre cas, le composant IbExtract donne satisfaction, et nous
nous contenterons de l'utiliser

3 - Le Projet Delphi
3.1 - L'Objectif
Nous devons crer un script qui permettra :

de crer les tables ainsi que toutes leurs annexes (gnrateurs, triggers etc)
de remplir les donnes des tables

3.2 - Le composant IbExtract


Ce composant a t construit uniquement pour extraire les donnes d'une base. Je
suppose que Jeff OVERCASH l'a spcialement cr pour crire IbConsole. Il
devrait donc ncessairement permettre de raliser notre travail. Le principal
problme rsoudre est de trouver les paramtres utiliser.
La documentation indique que

la syntaxe de base est:


nous appelons Extract de la faon suivante:
type TExtractObjectTypes= (eoDatabase, eoDomain, eoTable,
eoView, eoProcedure, eoFunction, eoGenerator,
eoException, eoBLOBFilter, eoRole, eoTrigger,
eoForeign, eoIndexes, eoChecks, eoData);
TExtractType= (etDomain, etTable, etRole, etTrigger,
etForeign, etIndex, etData, etGrant,
etCheck, etAlterProc);
TExtractTypes= Set of TExtractType;
procedure ExtractObject(ObjectType: TExtractObjectTypes;
ObjectName: String; ExtractTypes: TExtractTypes);

les paramtres sont:


o le type de donne extraire: eoDomain pour les
domaines, eoTable pour les tables etc
o si nous ciblons un objet particulier (une table, une procdure
catalogue etc), le nom de cet objet
o pour un objet donne, le dernier paramtre spcifie quelle donne (par
exemple pour une table, le triggers en utilisant etTrigger)

En ce qui nous concerne, nous esprions pouvoir tout extraire (les requtes de
cration de tables, les triggers etc et les donnes) par:
IbExtract1.ExtractObject(eoDataBase, '', []);

mais cet appel fournit bien le script de toutes les crations, mais pas le script pour
les donnes.

Aprs quelques essais, l'appel:


IbExtract1.ExtractObject(eoTabke, 'orders', [etData]);

fournit bien le script des donnes de la table ORDERS, mais aussi au dbut le script
de cration de cette table. En combinant les deux appels, nous obtenons finalement
notre rsultat.

3.3 - Le code d'extraction du schma


Voici le projet:
crez une nouvelle application Delphi et renommez-la "p_extract_ib_script"
ajoutez un tMemo qui recevra le script du schma
de la page "Interbase" de la Palette, slectionnez IbTransaction et posez-le sur la Forme
de la page "Interbase" de la Palette, slectionnez IbDataBase et posez-le sur la Forme
reliez IbTransaction1 et IbDataBase1
double cliquez sur IbDatabase1 pour afficher l'Editeur de Connection. Remplissez le nom
de la base, le nom de l'utilisateur, le mot de passe (SYSDBA), dcochez "LoginPrompt":

Cliquez "Ok" pour sauvegarder, double-cliquez nouveau et slectionnez "Test" pour


vrifier la connection
de l'onglet "Interbase" de la Palette slectionnez IbExtract et posez-le sur la Forme

posez un tButton sur la Forme, renommez-le "complete_schema_", crez son


vnement OnClick et crivez le code qui extrait toutes les requtes de cration du
schma:
procedure TForm1.complete_schema_Click(Sender: TObject);

begin
open_database_Click(Nil);
with IBExtract1 do
begin
ExtractObject(eoDataBase, '', []);
Memo1.Lines.Assign(Items);
end;
end; // complete_schema_Click
compilez, excutez et cliquez "complete_schema_"
le schma complet s'affiche dans Memo1

3.4 - Le code d'extraction des donnes


Pour extraire les donnes, nous allons procder en deux tapes:

trouver le nom de toutes les tables


pour chaque table, appeler ExtractObject avec le paramtres etData

L'extraction des noms de Table est ralis en appelant la procdure d'extraction


complte ci-dessus, et en extrayant le nom situ aprs chaque CREATE
DATABASE .

Voici comment placer le nom de chaque Table dans une tListBox:


ajoutez une tListBox au projet
ajoutez un tButton, renommez-le "tables_", et tapez le code qui
utilise ExtractObject pour rcuprer le script de cration et en extrait les noms de tables:
procedure TForm1.tables_names_Click(Sender: TObject);
var l_item: Integer;
l_the_line: String;
l_index: Integer;
l_table_name: String;
begin
open_database_Click(Nil);
ListBox1.Clear;
with IBExtract1 do
begin

ExtractObject(eoTable, '', []);


with Items do
for l_item:= 0 to Count- 1 do
begin
l_the_line:= Trim(Strings[l_item]);
if Pos('CREATE TABLE', l_the_line)= 1
then begin
System.Delete(l_the_line, 1, Length('CREATE TABLE'));
l_index:= 1;
l_table_name:=
f_string_extract_non_blank(l_the_line, l_index);
ListBox1.Items.Add(l_table_name);
end;
end;
end;
end; // tables_names_Click
ajoutez un tButton, renommez-le "all_table_data_", et tapez le code qui
utilise ExtractObject pour rcuprer le script d'insertion de donnes pour chaque Table:
procedure generate_insert_into(p_table_name: String;
p_c_strings: tStrings);
var l_item_index: Integer;
l_the_line: String;
begin
with Form1, IBExtract1 do
begin
ExtractObject(eoTable, p_table_name, [etData]);
// -- skip the schema
with Items do
begin
l_item_index:= 0;
while l_item_index< Count do
begin
l_the_line:= Strings[l_item_index];
if Pos('INSERT INTO', l_the_line)> 0
then Break
else Inc(l_item_index);
end;
// -- now get the INSERT INTO
while l_item_index< Count do
begin
l_the_line:= Strings[l_item_index];
p_c_strings.Add(l_the_line);
Inc(l_item_index);
end; // while l_item_index

end; // with Items


end; // with IBExtract1
end; // generate_insert_into
procedure TForm1.all_table_data_Click(Sender: TObject);
var l_item_index: Integer;
begin
with ListBox1 do
for l_item_index:= 0 to Count- 1 do
generate_insert_into(Items[l_item_index],
Memo1.Lines);
end; // all_table_data_Click
compilez, excutez et cliquez "complete_schema_"
voici une vue du projet:
<IMG SRC="_i_image.png" align=middle alt="image">
compilez, excutez et cliquez "all_table_data_"
voici une vue du projet, avec le script pour PARTS:

Fonctions Utilisateur Interbase (UDF) - John COLIBRI.

rsum : Cration et utilisation de fonctions utilisateur Interbase en Delphi


mots cl : UDF - Interbase - User Defined Function - DLL
logiciel utilis : Windows XP personnel, Delphi 6.0, Interbase 6

matriel utilis : Pentium 2.800 Mhz, 512 Meg de mmoire, 250 Giga
disque dur
champ d'application : Delphi 1 2006 sur Windows, Interbase
niveau : dveloppeur Delphi / Interbase
plan :
o Les UDF Interbase
o Le Projet Delphi
o Ajout d'une UDF
o Tlcharger le code source Delphi

1 - Les UDF Interbase


Interbase fut le premier moteur proposer l'ajout de fonctions externes. Les UDF
(User Defined Functions sont des routines places dans des DLL Windows qui
peuvent tre utilises dans des requtes SQL.
Nous nous sommes surtout intresss aux UDF car la base de donne du Portail
Asp Net Delphi emploie de telles fonctions. Pour pouvoir intgralement recrer le
Portail, il faut ajouter les UDF, d'o le prsent article.

2 - Ajout d'une UDF


2.1 - Principe des EXTERNAL FUNCTIONS
Voici la situation avant l'ajout d'une UDF:

Pour ajouter une UDF, il faut

crire la DLL qui exporte la routine qui nous intresse

placer la DLL dans le rpertoire des UDF Interbase

ajouter la routine en tant que EXTERNAL FUNCTION aux objets des bases
qui souhaitent l'utiliser

utiliser la routine dans nos requtes SQL

2.2 - La fonction utilise


Nous allons crire des UDF qui ajouterons Interbase quelques fonctions de
traitement de chanes de caractres:

RTRIM(p_chane) qui supprime les espaces situs aprs une chane


STRLEN(p_chane) qui retourne la taille d'une chane
RPAD(p_chane, p_taille_max, p_caractre) qui complte une chane avec
le caractre p_caractre pour arriver la taille p_taille_max
SUBSTR(p_chane, p_position, p_nombre) qui extrait
de p_nombre caractres partir de p_position

3 - Le Projet Delphi
3.1 - L'criture de l'UDF
Les fonctions externes doivent tre places dans une .DLL Windows. Cette .DLL
peut tre ralise avec n'importe quel outil.
En gnral, n'importe lequel n'est pas assez bon, et c'est pourquoi nous
utiliserons Delphi.
L'unit qui calcule les chanes est la suivante:

unit u_udf_string;
interface
uses SysUtils, Windows, Classes;

function f_trim_right(p_pchar: PChar): PChar; cdecl; export;


function f_right_pad(p_pchar: PChar; var pv_target_length: Integer;
p_pad_pchar: PChar): PChar; cdecl; export;
function f_string_length(p_pchar: PChar): Integer; cdecl; export;
function f_sub_string(p_pchar: PChar;
VAR pv_position, pv_length: Integer): PChar; cdecl; export;
implementation
uses u_udf_globals;
function f_trim_right(p_pchar: PChar): PChar;
begin
Result:= f_build_pchar(PChar(TrimRight(String(p_pchar))), nil, 0);
end; // f_trim_right
function f_right_pad(p_pchar: PChar; var pv_target_length: Integer;
p_pad_pchar: PChar): PChar; cdecl; export;
// -- complement the string to the right up to the desired length
var l_string, l_padded_string: String;
l_padded_length, l_length, l_index: Integer;
begin
l_string:= String(p_pchar);
l_length:= Length(l_string);
l_padded_string:= String(p_pad_pchar);
l_padded_length:= Length(l_padded_string);
if (l_length< pv_target_length) and (l_padded_length<= pv_target_lengthl_length)
then
begin
// -if padding is a string, can return a string longer than pv_target_length
for l_index:= 1 to (pv_target_length- l_length) div l_padded_length do
l_string:= l_string+ l_padded_string;
end;
Result:= f_build_pchar(PChar(l_string), nil, 0);
end; // f_right_pad
function f_string_length(p_pchar: PChar): Integer;
begin
Result:= StrLen(p_pchar);
end; // f_string_length

function f_sub_string(p_pchar: PChar;


VAR pv_position, pv_length: Integer): PChar; cdecl; export;
var l_string, l_substring: String;
begin
l_string:= String(p_pchar);
l_substring:= Copy(l_string, pv_position, pv_length);
Result:= f_build_pchar(PChar(l_substring), nil, 0);
end; // f_sub_string
end.

Cette unit utilise une unit d'allocation de pChar:


unit u_udf_globals;
interface
uses Windows, SysUtils;

function f_build_pchar(p_source_pchar, p_destination_pchar: PChar;


p_length: DWORD): PChar;
implementation
function malloc(Size: Integer): Pointer; cdecl; external 'msvcrt.dll' ;
function f_build_pchar(p_source_pchar, p_destination_pchar: PChar;
p_length: DWORD): PChar;
begin
result:= p_destination_pchar;
if (result= nil)
then
begin
if (p_length= 0)
then p_length:= StrLen(p_source_pchar)+ 1;
result:= malloc(p_length);
end;
if (p_source_pchar<> result)
then
begin
if (p_source_pchar= nil) or (p_length= 1)
then result[0]:= #0
else Move(p_source_pchar^, result^, p_length);
end;
end; // f_build_pchar
end.

Et le texte de la DLL est le suivant:


library d_string_udf;
uses
SysUtils,
Classes,
u_udf_string;

exports
f_trim_right,
f_right_pad,
f_string_length,
f_sub_string ;
end.

Il faut donc
compiler cette librairie
compiler le fichier d_string_udf.dll dans le rpertoire d'Interbase qui contient toutes les .DLL qui
seront intgres au moteur. Dans notre cas, il s'agit de

C:\Program Files\Borland\InterBase\UDF\
rebooter le PC pour que le moteur Interbase prenne cette .DLL en compte

3.2 - Ajout des EXTERNAL FUNCTIONS


3.2.1 - Cration d'une base

Nous allons crer une base spcialement pour tester nos UDF. Vous pouvez trs
bien utiliser une base prexitante de votre choix.
Le dtail de la cration de la base, de la cration et du remplissage d'une table
contenant quelques CHAR et VARCHAR se trouve dans le .ZIP tlchargeable.
Quoi qu'il en soit, il s'agit d'une base de cours de formations contenant
la table suivante:

CREATE TABLE training

(
f_id INTEGER,
f_name CHAR(30),
f_location VARCHAR(10),
f_days INTEGER,
f_price FLOAT
)

remplie avec les lments suivants:


100
101
102
103
104
105

Interbase Client Server


ADO.Net Sql Server
ASP.Net Oracle
TCP / IP Internet
Turbo Delphi Design Patterns
Delphi OO Programming

Paris
London
Hong Kong
Montreal
Sao Paulo
Dallas

3
3
3
3
3
3

1400
1400
1400
1400
1400
1400

Voici d'ailleurs le projet avec la table de test:

3.2.2 - Ajout des UDF

Nous allons ajouter aux UDF connues du moteur Interbase la fonction qui calcule
la taille d'une string:

le nom de la fonction est StrLen


la fonction provient de la fonction Delphi f_string_length, qui est situe
dans la .DLL "d_string_udf.dll"

La requte SQL pour ajouter cette UDF est:

DECLARE EXTERNAL FUNCTION StrLen


CSTRING(32767)
RETURNS INTEGER BY VALUE
ENTRY_POINT 'f_string_length' MODULE_NAME 'd_string_udf'

Pour ajouter cette UDF, il suffit d'envoyer cette requte au moteur:


const
k_create_strlen=
'DECLARE EXTERNAL FUNCTION STRLEN'
+ ' CSTRING(32767)'
+ ' RETURNS INTEGER BY VALUE'
+ ' ENTRY_POINT ''f_string_length'' MODULE_NAME ''d_string_udf''';

procedure TForm1.create_udf_Click(Sender: TObject);


begin
initialize_ib_database(k_database_name, k_user, k_password, IbDatabase1);
if f_open_ib_database('', IbDatabase1)
then begin
display('ok_open');
with IbSql1 do
begin
Close;
Database:= IbDatabase1;
end;
if f_execute_ibsql(IbSql1, k_create_strlen)
then display('ok_strlen');
end;
end; // create_udf_Click

3.2.3 - Suppression d'une fonction externe

Pour retirer cette fonction du moteur Interbase, nous appelons naturellement:

DROP EXTERNAL FUNCTION StrLen

Le code se trouve dans le .ZIP tlchargeable

3.2.4 - Liste des fonctions

Et pour vrifier si la fonction est bien enregistrer, nous utilisons un


composant IbExtract en lui demandant d'afficher les UDF:
procedure TForm1.display_udf_Click(Sender: TObject);
// -- extract all the stored procedures
var l_item: Integer;
begin
initialize_ib_database(k_database_name, k_user, k_password, IbDatabase1);
if f_open_ib_database('', IbDatabase1)
then begin
with IBExtract1 do
begin
ExtractObject(eoFunction, '');
with Items do
for l_item:= 0 to Count- 1 do
display(Strings[l_item]);
end; // with IBExtract1
end;
end; // display_udf_Click

Voici le projet ce stade:

3.2.5 - Test des UDF

Pour utiliser une fonction externe, nous l'appelons depuis une requte SQL, que
celle-ci soit lance depuis le Client ou depuis une procdure catalogue.
Dans le cas de StrLen, nous pouvons appeler, par exemple:

SELECT StrLen(f_location) FROM training WHERE f_id= 105

Depuis Delphi, nous utilisons un IbQuery et rcuprons la valeur dans la premire


colonne de la rponse:
const k_select_strlen= 'SELECT F_LOCATION, STRLEN(F_LOCATION) '
+ ' FROM TRAINING WHERE f_id= 105';

procedure TForm1.select_strlen_Click(Sender: TObject);


var l_rtrim_name, l_name: String;
begin
initialize_ib_database(k_database_name,
k_user, k_password, IbDatabase1);
if f_open_ib_database('', IbDatabase1)
then begin
display('ok_open');
open_ibquery(display_udf_ibquery_, k_select_strlen);
l_name:= display_udf_ibquery_.Fields[0].AsString;
l_rtrim_name:= display_udf_ibquery_.Fields[1].AsString;
display(IntToStr(Length(l_name))+ ' |'+ l_name+ '|');
display(l_rtrim_name);
end;
end; // select_strlen_Click

3.2.6 - Test pralable des fonctions

Pour certaines fonctions (RPad, par exemple), nous avons eu un peu de mal faire
fonctionner le tout. Nous avons alors test la fonction de la .DLL sparment, pour
nous assurer que le code tait correct.
Pour cela nous avons utilis une unit d'importation qui permettait d'appeler
facilement les fonctions de la .DLL:

unit u_import_udf_string;
interface
const k_udf_string= 'd_string_udf.dll';

function f_trim_right(p_pchar: PChar): PChar;


cdecl; external k_udf_string;
function f_right_pad(p_pchar: PChar; var p_length: Integer;
p_pad_pchar: PChar): PChar; cdecl; external k_udf_string;
function f_string_length(p_pchar: PChar): Integer;
cdecl; external k_udf_string;
function f_sub_string(p_pchar: PChar; p_position,
p_length: Integer): PChar; cdecl; external k_udf_string;
implementation
end. // u_zzz

Pour la fonction RPAD, nous avions mme du mal comprendre ce que la fonction
faisait (c'tait une fonction que nous n'avions pas crite initialement), et nous avons
test la fonction dans le projet mme (pour bnficier de tous les affichages de mise
au point), avant de la rinjecter dans la .DLL

3.3 - Surprises, Surprises


Parmi les quelques curiosits:

les valeurs entires doivent tre passes en paramtre VAR


aprs avoir plac la .DLL dans le rpertoire UDF d'Interbase, il faut rebooter
la machine
comme toujours en Interbase, il vaut mieux rdiger les identificateurs des
requtes qui crivent sur la base en MAJUSCULES (nous crons tout en
minuscule, mais quelqu'un passe le tout en majuscules. Les SELECT ne
posent pas de problmes, mais les DROP ou autres exigent des majuscules)
lorsque nous plaons certaines UDF entre SELECT et FROM, elles ne sont
pas prises en compte.
RTRIM n'est pas utile dans le WHERE: apparemment, Interbase supprime les
espaces droite sans avoir besoin de RTRIM
pour SUBSTR, la fonction extrait bien la sous-chane, mais retourne une
chane de la taille maximale (85 dans notre cas), avec au dbut la souschane
la fonction SUBSTR fournie avec Interbase a une autre smantique que
notre SUBSTR: nous utilisons

SUBSTR(p_chane, p_position, p_taille)


et eux
SUBSTR((p_chane, p_indice_debut, p_indice_fin)

Car il y a, en effet des UDF fournies par dfaut par Interbase. Pour l'anectdote,
nous nous en sommes apperus, car le projet fonctionnait sans avoir copier la
.DLL dans le rpertoire UDF, car les fonctions existaient dans la .DLL par dfaut
IB_UDF.DLL. Toutes sauf RPAD, qui a donc provoqu une erreur (pas lors
de CREATE EXTERNAL, mais lors de l'appel dans un SELECT )
La preuve, voici le rpertoire des UDF (avant ajout de notre .DLL):

Pour connatre le contenu de ces UDF, nous avons utiliser notre analyseur de
fichier PE (Portable Executable), qui affiche les fonctions exports par une .DLL
(ou tout autre .EXE). Voici ce qu'il nous a fourni pour IB_UDF.DLL:

Mais quelle est la syntaxe de ces UDF: en fait elles sont fournies comme exemple
d'UDF dans le rpertoire:

Vous trouverez le source (C) des UDF, avec des commentaires bien crits, ainsi
que le script qui incorpore ces UDF dans une base quelconque.

SQL Script Executer - John COLIBRI.

rsum : un utilitaire qui permet d'excuteur des scripts Sql Interbase


mots cl : SQL Script - Interbase - excution de script - script parser
logiciel utilis : Windows XP personnel, Delphi 6.0, Interbase 6/7
matriel utilis : Pentium 2.800 Mhz, 512 Meg de mmoire, 250 Giga
disque dur
champ d'application : Delphi 1 2006, Turbo Delphi sur Windows
niveau : dveloppeur Delphi / Interbase
plan :
o Introduction
o Principe
o Le Projet Delphi
o Tlcharger le code source Delphi

1 - Introduction

Un excuteur de script SQL est un programme qui permet d'excuter une ou


plusieurs requtes SQL provenant d'un fichier ASCII.
Actuellement, les composants d'accs aux moteurs Sql grs par Delphi ne
permettent d'excuter qu'une seule requte la fois. Cela convient parfaitement
pour rcuprer par un seul SELECT toute une Table, ou pour modifier des lignes
par un seul UPDATE. Mais lorsqu'il s'agit de crer une nouvelle base, avec cration
de Tables, procdures catalogues, Triggers, et remplissage initial des Tables, il est
plus efficace de recourir des scripts.
De nombreux outils, dont IbConsole qui est fourni avec Delphi, permettent de
lancer des scripts.
Nous prfrons utiliser notre propre mcanique, et pour deux raisons au moins

la plupart des outils de gestion de base de donnes ncessitent la frappe


clavier de nombreux paramtres: nom d'utilisateur, mot de passe, nom du
fichier contenant le script etc. Si tout se pass bien, la rigueur. Mais si le
script contient des erreurs, il faut morceler le texte en plusieurs parties,
lancer ces parties les unes aprs les autres, recommencer aprs avoir
corrig les erreurs du script. Cela devient assez vite fatiguant
certains de nos clients doivent pouvoir initialiser une base (nouvel exercice,
ajout d'un tablissement etc). Nous prfrons leur fournir un
logiciel Delphi qui s'en charge, plutt que de leur demander de
lancer IbConsole puis cliquer ici ou l

2 - Principe
Le texte du script contient les requtes les unes aprs les autres, avec un
terminateur quelconque aprs chaque requte.
En ce qui concerne Interbase:

le terminateur initial est ";"


/* Table: PORTAL_ROLES, Owner: SYSDBA */

CREATE TABLE "PORTAL_ROLES"


(

"ROLEID" INTEGER NOT NULL,


"PORTALID" INTEGER NOT NULL,
"ROLENAME" VARCHAR(50),
PRIMARY KEY ("ROLEID")
);
/* Table: PORTAL_USERS, Owner: SYSDBA */
CREATE TABLE "PORTAL_USERS"
(
"USERID" INTEGER NOT NULL,
"NAME" VARCHAR(50),
"APASSWORD" VARCHAR(50) NOT NULL,
"EMAIL" VARCHAR(100) NOT NULL,
PRIMARY KEY ("USERID"),
CONSTRAINT "K_PORTAL_USERS_MAIL" UNIQUE ("EMAIL")
);

Ici les deux crations de table sont termines par ";"


comme certaines procdures catalogues contiennent aussi des ";", la
convention courante (en tout cas celle d'IbConsole) est de changer
temporairement de terminateur en utilisant la pseudo instruction SET
TERM (SET TERMinator)
/* generators */

CREATE GENERATOR "USER_ITEMID";


SET TERM ^ ;
/* Stored procedures */
CREATE PROCEDURE "CLEANUP"
AS
BEGIN EXIT; END ^
CREATE PROCEDURE "PORTAL_GETSINGLEROLE"
(
"ROLEID" INTEGER
)
RETURNS
(
"ROLENAME" VARCHAR(50)
)
AS
BEGIN EXIT; END ^

Notez que

o
o

SET TERM se termine (encore) par ";"


les deux requtes qui suivent comportent les ";" des procdures
catalogues, mais ces requtes sont termines prsent par "^"

Le travail effectuer est alors assez simple:

supprimer les commentaires (textes entre /* et */)


dbiter le texte en requtes en analysant ce qui se trouve termin par le
terminateur courant (";" au dpart, puis la valeur indique par SET
TERM par la suite)

Il suffit donc d'analyser le script et de lancer chaque requte dtecte.

3 - Le Projet Delphi
Le travail de l'analyseur
Il s'agit l d'une version de plus du scanner de base, qui dtecte les commentaires
de script, et les mots cls qui nous intressent: SET TERM . Nous avons aussi
dcouvert que d'autres pseudo instructions devaient tre traites (limines, dans
notre cas):

SET SQL DIALECT


COMMIT WORK
SET AUTODDL OFF

Nous incluons donc ces mots cls dans l'analyseur.

L'analyseur de requte
Voici l'analyseur

tout d'abord la dfinition de la CLASSe:

t_symbol_type= (e_unknown_symbol,
e_identifier_symbol, e_integer_symbol, e_operator_symbol,
e_string_litteral_symbol,
e_comment_symbol,
e_end_symbol);

c_ib_script= class(c_text_file)
m_symbol_type: t_symbol_type;
m_blank_string, m_symbol_string: String;
_m_previous_index: Integer;
m_c_result_list: tStringList;
m_request_index: Integer;
m_is_SET_TERM, m_is_COMMIT_WORK, m_is_SET_AUTODDL: Boolean;
m_terminator: Char;
Constructor create_ib_script(p_name, p_file_name: String);
function f_initialized: Boolean;
procedure initialize_scanner;
function f_read_symbol: Boolean;
procedure test_scanner;
function f_remove_comment: String;
procedure initialize_request_scanner;
function f_next_request: String;
function f_get_request: String;
Destructor Destroy; Override;
end; // c_ib_script

puis la fonction qui rcupre une requte:


function c_ib_script.f_get_request: String;
var l_request: String;

// -- here get_request()
begin // f_get_request
m_is_SET_TERM:= False;
m_is_COMMIT_WORK:= False;
m_is_SET_AUTODDL:= False;

repeat
get_request;
until (m_symbol_type= e_end_symbol)
or (m_symbol_type<> e_comment_symbol);
if m_symbol_type= e_end_symbol
then l_request:= '';
Result:= l_request;
end; // f_get_request

et cette fonction appelle:


procedure get_request;
var l_previous_identifier: String;

function f_is_SET_TERM: Boolean;


begin
Result:= (l_previous_identifier= 'SET')
and
(m_symbol_string= 'TERM')
end; // f_is_SET_TERM
function f_is_COMMIT_WORK: Boolean;
begin
Result:= (l_previous_identifier= 'COMMIT')
and
(m_symbol_string= 'WORK')
end; // f_is_COMMIT_WORK
function f_is_SET_AUTODDL: Boolean;
begin
Result:= (l_previous_identifier= 'SET')
and
(m_symbol_string= 'AUTODDL')
end; // f_is_SET_AUTODDL
var l_display: Boolean;
begin // get_request
l_request:= '';
l_display:= False;
// -- break if found m_terminator
repeat
f_read_symbol;
if l_display

then display(Format('%7d ', [m_buffer_index])+ m_symbol_string);


if m_symbol_type<> e_comment_symbol
then begin
if f_is_SET_TERM
then begin
// -- get the terminator
f_read_symbol;
m_terminator:= m_symbol_string[1];
display('// -- SET TERM '+ m_symbol_string);
m_is_SET_TERM:= True;
// -- skip actual terminator and exit loop
f_read_symbol;
break;
end else
if f_is_COMMIT_WORK
then begin
m_is_COMMIT_WORK:= True;
end else
if f_is_SET_AUTODDL
then begin
// -- skip "OFF ;"
f_read_symbol;
m_is_SET_AUTODDL:= True;
end else
if m_symbol_string= m_terminator
then begin
Break;
end
else l_request:= l_request+ m_blank_string+ m_symbol_string;
l_previous_identifier:= m_symbol_string;
end;
if m_buffer_index= _m_previous_index
then begin
display_bug_stop('no_progress');
Break;
end;
_m_previous_index:= m_buffer_index;
until m_symbol_type= e_end_symbol;
end; // get_request

Lancer les requtes


Le projet principal charge le texte du script et rcupre les requtes
procedure TForm1.execute_script_Click(Sender: TObject);

// -- here execute_request()
var l_request: String;
l_trimmed_request: String;
begin // execute_script_Click
original_memo_.Lines.LoadFromFile(g_script_path+ g_script_file_name);
if not FileExists(g_script_path+ g_script_file_name)
then display_bug_stop('not_found '+ g_script_path+ g_script_file_name);
connect_database_Click(Nil);
with c_ib_script.create_ib_script('ib_script',
g_script_path+ g_script_file_name) do
begin
if f_initialized
then begin
initialize_scanner;
repeat
l_request:= f_get_request;
if not (m_is_SET_TERM or m_is_COMMIT_WORK or m_is_SET_AUTODDL)
then begin
l_trimmed_request:= f_add_return_line_feed(l_request);
l_trimmed_request:= f_change_returns_in_spaces(l_trimmed_request);
l_trimmed_request:= Trim(f_remove_double_spaces(l_trimmed_request));
if exec_.Checked
then begin
if (Pos('DIALECT', l_request)<= 0)
and
(Trim(l_request)<> '')
then
execute_request(l_request)
end
else display(l_request);
end;
until l_request= '';
end
else display_bug_stop('not_found '+ g_script_path+ g_script_file_name) ;
end; // with c_ib_script

end; // execute_script_Click

et chaque requte est lance par:


procedure execute_request(p_request: String);
begin
try
if IbDatabase1.DefaultTransaction.InTransaction
then IbDatabase1.DefaultTransaction.Commit;

IbDatabase1.DefaultTransaction.StartTransaction;
IbSql1.Sql.Text:= p_request;
// -- for stored procedures
IbSql1.ParamCheck:= False;
IbSql1.ExecQuery;
IbDatabase1.DefaultTransaction.Commit;
except
on e: Exception do
begin
display_bug_stop('*** pb_'+ e.Message);
end;
end; // try except
end; // execute_request

Min manuel
Le script

Nous avons choisi de recrer la base MASTAPP.


Le script est obtenu en utilisant l'extracteur de script. Le script est contenu dans le
fichier schema_IB_MASTSQL_6.txt (dans le .ZIP tlchargeable ci-dessous)

HowTo

Pour utiliser le logiciel


compilez le projet
slectionnez le rpertoire et la base l'aide du tDirectoryListBox de l'onglet "database". Puis

si la base existe slectionnez le fichier


sinon, crez la base en cliquant "create_base_" dans l'onglet "script_"

slectionnez le script qui vous intresse en utilisant le tDirectoryListBox et le tFileListBox de


l'onglet "script_"
slectionnez "exec_" puis cliquez "execute_script_"

Voici l'image du projet:

Quelques soucis
Parmi les petites misres:

il a fallu veiller restaurer les "Return LineFeed" complets (certains scripts


ne contenaient, ici ou l, que des LineFeed UNIX

les valeurs littrales des dates doivent tre un format commestible pour
le moteur. Donc soit "mois, jour, anne", soit, et c'est nouveau pour nous,
"anne, mois, jour"
les guillemets sont placs dans le script du gnrateur de script, mais
doivent tre retirs pour l'excution des scripts
les blobs doivent faire l'objet d'un traitement spar (mettant en oeuvre
des tStreams)

En tant un rien cynique, nous pourrions rcuprer les sources de IbConsole, qui
sait bien franchir toutes ces barrires sans souci. En attendant, cet utilitaire a
rempli les missions que nous avions envisages.

Tutorial Interbase - John COLIBRI.

mots cl:tutorial - Interbase - Client Serveur


logiciel utilis: Windows XP, Delphi 6, Interbase 6
matriel utilis: Pentium 1.400Mhz, 256 M de mmoire
champ d'application: Delphi 1 7 sur Windows, Kylix
niveau: dbutant Delphi et Base de Donnes
plan:
o Introduction
o Installation
o Crer la Base
o Crer une Table
o Ajouter des Donnes
o Lire et Afficher
o Modifier des Donnes
o Effacer de Donnes
o Plusieurs Tables
o Tlcharger les Exemples
o Conclusion

1 - Introduction
Ce tutorial va vous indiquer comment utiliser le Serveur Interbase fourni avec
Delphi. Ce tutorial a plusieurs objectifs:

vous prsenter intgralement comment raliser des applications utilisant des


bases de donnes Sql en Delphi. Tous les exemples ont t cods et les
sources sont tlchargeables.

effectuer toutes les oprations en Delphi, plutt que de prsenter une srie
d'outils annexes dont la prsentation alongerait l'expos
insister sur les points qui posent problme (cration d'une base, chane de
connection, modification de dbGrid) plutt que sur les composants visuels
assez intuitifs au demeurant
prsenter les composants rellement utiliss pour grer des tables Sql
(des tIbQuery) plutt que des composants gnriques (tIbTable) dont les
traitements automatiques masquent le fonctionnement rel d'un Serveur Sql
limiter au maximum le nombre de composants utiliss pour manipuler les
donnes du Serveur sans perdre en fonctionnalit

Si aprs ce premier contact vous souhaitez approfondir vos connaissances, nous


vous proposons soit des formations ou des prestations de programmation et
d'assistance.
Nous nous adressons pour ce tutorial un programmeur Delphi:

ayant une ide lmentaire de Delphi: Palette, Inspecteur, OnClick. Tout le


reste sera expliqu
n'ayant pas ncessairement de connaissance SQL. Le fonctionnement des
requtes et leur syntaxe sera prsent

2 - Installation
2.1 - Principe
Le logiciel Interbase comporte essentiellement deux parties:

le programme Serveur (le moteur), qui en gnral est plac sur un PC distant
le programme Client, qui dialogue avec les programme applicatifs
(Delphi ou autre).

Il faut donc installer les deux parties (sur la mme machine ou sur des PC spars).
Interbase peut fonctionner selon deux modes:

le mode dit "local", pour lequel le moteur et le programme client sont sur le
mme PC. La machine n'a pas besoin d'avoir les couches rseau (TCP\IP,
Novell ou autre), ni de carte rseau (carte Ethernet ou liaison srie). Le
programme applicatif communique avec le Client, qui appelle directement
les routines du serveur (sans passer par des appels TCP\IP)

le mode Client Serveur


o les communications entre la DLL client et le Serveur passent par les
couches rseau de Windows: il faut que ces couches rseau soient
installes (mme si le Serveur et le Client sont sur le mme PC)
o le programme applicatif devra dsigner le serveur en utilisant l'adresse
IP du serveur.

Dans les deux cas, l'installation se fait en utilisant l'installateur du CD Delphi (ou
du CD Interbase). Nous prsenterons l'instalation partir du CD Delphi.

2.2 - Installation avec Delphi


Lors de l'installation de Delphi, Installshield propose d'installer aussi Interbase.

Vous pouvez rpondre oui et Interbase sera install


Si vous avez rpondu "Non", voici comment procder:

2.3 - Installation du Mode Local


Pour installer Interbase en mode local:
insrez le CD Delphi
slectionnez "Interbase Desktop Edition"
rpondez oui toutes les questions
vous pouvez vrifier que

le rpertoire "c:\Program Files\Borland\" contient bien un sous-rpertoire


"Interbase"
le rpertoire "c:\Windows\SYSTEM\" contient bien la DLL client "gds32.dll"
le menu dmarrer propose prsent une option "Interbase" avec "Interbase
Server Manager". Cliquez sur ce choix, et le gestionnaire du serveur apparat:

2.4 - Installation du Mode Distant


Installez le Serveur sur le PC serveur, en procdant comme ci-dessus, mais
slectionnez "Interbase 6 Server". La partie Client installe sur le PC
du Serveur sera utilise pour les logiciels de gestion du Serveur
Installez le Client sur le PC client:
slectionnez "Interbase 6 Server"
aprs les dialogues de copyright ou autres apparatra:

slectionnez "Interbase Client"

2.5 - Suppression d'Interbase


Pour supprimer Interbase:
ouvrez le Serveur Manager et cliquez "Stop" (pour arrter le moteur)
ouvrez le panneau de configuration, slectionnez "Ajout / Suppression de logiciel",
slectionnez "Interbase" et cliquez Oui partout

3 - Crer la Base
3.1 - SQL et la cration de bases
Pour crer une base, il faut, pour tous les moteurs, utiliser des outils spciaux, le
langage SQL ne contenant pas de requte spcifique pour cette opration
Pour Interbase, ce sont des primitives de l'API native du moteur qui permettent
cette cration. Elle n'est pas possible depuis les composants gnrique d'accs aux
donnes (tTable, tQuery). En revanche, Jeff Overcash a ajout tIbDatabase la
mthode qui utilise les API Interbase natifs et permet la cration de la base.

Les lments fournir sont les suivants;

le chemin DOS o sera plac le fichier .GDB


le dialecte Interbase utiliser. En gros, le dialecte dtermine le traitement de
certains types de donnes Interbase (en dialecte 3 nous pouvons utiliser
des Integer 64 bits et des tTimeStamps, minuscule ou majuscule sont
reconnus etc). Nous choisirons le dialecte 3 (le plus rcent).
le nom d'utilisateur et le mot de passe du Serveur. Ce sont les noms du
serveur par dfaut (la base n'tant pas encore cre, elle)
des paramtres d'optimisation, tels que la taille des pages du cache

Plus prcisment:

le chemin est plac dans tIbDatabase.DataBaseName


le dialecte est plac dans tIbDatabase.SqlDialect
le nom d'utilisateur et le mot de passe, ainsi que les autres paramtres sont
placs dans la proprit fourre-tout Params
la mthode de cration est simplement:
tIbDatabase.CreateDataBase

3.2 - Cration de la base


Nous allons crer une base contenant les stages offerts par l'Institut Pascal. Nous
placerons cette base dans le rpertoire "..\data\" (situ au mme niveau que le
.EXE. Le .ZIP tlchargeable crera ce rpertoire automatiquement). Le nom du
fichier sera "Institut_Pascal.GDB".
Pour crer la base:
crez une nouvelle application et appelez-la "p_ib_create_base"
slectionnez dans la page "Interbase" de la Palette le composant tIbTransaction:

et posez ce composant sur la tForm


slectionnez de mme une tIbDatabase

et placez-la sur la tForm


initialisez IbDatabase.DefaultTransaction vers IbTransaction1
utilisez un tButton pour crer la base:

placez un tButton, nommez-le create_base_, et crez sa mthode OnClick


voici le code de cette mthode:
procedure TForm1.create_database_Click(Sender: TObject);
begin
with IbDatabase1 do
begin
DatabaseName:= '..\data\Institut_Pascal.gdb';
SqlDialect:= 3;
Params.Add('USER "SYSDBA"');
Params.Add('PASSWORD "masterkey"');
Params.Add('PAGE_SIZE 4096');
CreateDatabase;
end; // with IbDataBase1
end; // create_database_Click

compilez et excutez
cliquez Button1
le rpertoire ..\data\ contient bien le fichier "Institut_Pascal.GDB" dont la taille est
d'environ 580 K

Notez que:

si le fichier formation.gdb existait dj il faut tester sa prsence (FileExists)


et l'effacer (Erase)
si nous souhaitons rpter la cration, il faudrait purger les Params avant
tout ajout par Add (Params.Clear)

Vous pouvez tlcharger ce projet "ib_create_base.zip".

3.3 - Connection une base


tIbDatabase est utilis par la suite pour assurer les changes entre notre application
et la base que nous venons de crer.
Ce composant doit tre initialis avec:

dans DataBaseName, la chane de connection qui est


o si nous travaillons en local, le chemin:
c:\programs\interbase\data\Institut_Pascal.gdb
ou
..\data\Institut_Pascal.gdb
o l'URL du PC qui hberge le Serveur:
127.0.0.1\ c:\programs\interbase\data\Institut_Pascal.gdb
HostName\ c:\programs\interbase\data\Institut_Pascal.gdb
www.jcolibri.com\
c:\programs\interbase\data\Institut_Pascal.gdb
dans Params, les paramtres propres Interbase. Dans notre cas, les
paramtres par dfaut du Serveur, plus le nom d'utilisateur et le mot de
passe.

Une fois les proprits initialises, nous pouvons tester la connection en


basculant tIbDatabase.Connected sur True.
A titre de vrification:
posez un second tIbDataBase sur la forme
initialisez IbDatabase2.DatabaseName avec
..\data\Institut_Pascal.gdb
cliquez deux fois sur IbDatabase2 pour ouvrir l'diteur de tIbDatabase
Delphi prsente le dialogue suivant:

slectionnez "User Name" et tapez SYSDBA


slectionnez "Password" et tapez masterkey
supprimez la coche de "Login Prompt"
le rsultat est:

fermez l'diteur
slectionnez Connected et basculez sa valeur sur True
au bout d'un "petit" instant, la valeur bascule sur True

Notez que:

la connection est le test IMPERATIF pour vrifier que nous pouvons


travailler en Interbase. Nous recommandons d'effectuer cette connection
systmatiquement avant de poser des tonnes de composants sur la tForm ou
d'crire des milliers de lignes
si nous connectons la base en mode conception, Delphi ouvrira aussi la base
lorsque l'excution sera lance. Ces deux connections sont comptabiliss par
le gestionnaire de licences Interbase comme 2 connections spares (ce sont
bien deux processus Windows). Si vous travaillez en utilisant la
version Delphi d'Interbase, il vaut mieux fermer la connection en mode
conception, puis la rouvrir lors de l'excution par:
tIbDatabase.Open;

4 - Crer une Table


4.1 - Principe
Nous allons crer une table contenant pour chaque formation:

un code (par exemple 8)


un nom (par exemple "Delphi Interbase")
un prix (par exemple 1.400)

Pour cela nous devons envoyer une requte en langage SQL vers
le Serveur Interbase.
La syntaxe de cette requte est:
CREATE TABLE formations
(f_numero INTEGER, f_nom CHARACTER(11), f_jours INTEGER,
f_prix NUMERIC(5, 2) )

Il suffit donc de choisir un nom de table, et le nom de chaque colonne avec son
type.
Parmi les types autoriss par Interbase citons:

INTEGER pour les valeurs entires 32 bits


SMALLINT pour les valeurs entires 16 bits
NUMERIC(decimales, prcision) pour une valeur numrique flottante

DATE pour une date


CHARACTER(taille) pour des caractres

Pour envoyer cette requte vers le Serveur:

nous utilisons un tIbDataBase qui assurera la connection vers le Serveur


nous utilisons un tIbQuery
o nous le relions tIbDatabase
o nous plaons la requte SQL dans sa proprit IbQuery.SQL (via
l'Inspecteur ou en code)
o nous appelons tIbQuery.ExecSql
la cration n'est visible par tout le monde que si la transaction qui est utilise
pour la cration de la table est confirme

4.2 - Utilisation de SQL


La requte envoyer au Serveur est place dans tIbQuery.Sql. tIbQuery.Sql est un
descendant de tStrings. Nous pouvons donc utiliser, par exemple:
IbQuery1.Sql.Add('CREATE TABLE formations (f_numero INTEGER, f_nom CHAR
ACTER(11))');

Pour construire la requte:

nous pouvons utiliser Add pour ajouter une ligne de requte, Text pour
affecter une requte, Clear pour purger tout texte antrieur, ou
mme LoadFromFile pour lire un fichier .txt contenant la requte:
IbQuery1.Sql.LoadFromFile('cree_formation.txt');

La mise en page n'a aucune importance pour le Serveur: la requte peut tre
rpartie en plusieurs lignes:
tIbQuery.Sql.Add('CREATE TABLE');
tIbQuery.Sql.Add(' formations ');
tIbQuery.Sql.Add(' (f_numero INTEGER, f_nom CHARACTER(11))');

nous pouvons entrer la requte en utilisant l'Inspecteur d'Objet. Si nous


cliquons sur Sql, Delphi affiche:

et vous pouvez taper manuellement la requte dans la partie "SQL"

nous pouvons aussi crer la String en plusieurs tapes par toutes les
primitives de String telles que la concatnation, Insert, le test par Pos ou
autre. Par exemple:
l_requete:= 'CREATE TABLE '+ Edit1.Text+ ' (';
l_requete:= l_requete+ Edit2.Text+ ')';
tIbQuery.Sql.Add(l_requete);

4.3 - Comment a Marche


Au niveau fonctionnement:

lorsque nous construisons la requte par IbQuery.Sql.Add, ce texte de


requte est ce stade une tStrings en mmoire de notre PC.

C'est une tStrings comme n'importe quelle autre. Ce qui explique qu'aucune
vrification de syntaxe n'est effectue.
Lorsque nous excutons:
IbQuery1.ExecSql;

alors:
o
o

la requte est envoye au Serveur via le Client


le Serveur traite la requte et:
cre la table si la requte est correcte:

retourne une erreur transforme par Delphi en exception en cas


de problme

Notez que l'envoi de toute requte qui modifie des donnes du Serveur (et la
cration d'une nouvelle table est bien une modification) ne peut se faire que par du
code (PAS en basculant IbQuery1.Active sur True en mode conception)

4.4 - Les Transactions


En Interbase, toutes les requtes sont cres dans le cadre d'une transaction. Les
transaction sont un mcanisme qui garantit que plusieurs sont ralises en entier ou
annules. L'archtype est le transfert bancaire: si nous dbitons DUPOND pour
crditer MARTIN, le systme doit garantir que soit les deux modifications sont
ralise ou aucune ne l'est.
En Delphi, les composants de bases de donnes utilisent par dfaut des transactions
transparentes pour le programmeur, mais nous pouvons grer les transactions nousmme. C'est ce qui est recommand pour Interbase.
En InterbaseExpress, nous utilisons un composant tIbTransaction que nous
connectons tIbDatabase.DefaultTransaction. Les primitives sont:

tIbTransaction.StartTransaction pour dmarrer une nouvelle transaction


avant une ou plusieurs oprations
tIbTransaction.Commit pour confirmer la suite d'opration,
ou tIbTransaction.RollBack pour tout annuler.
StartTransaction et Commit ou RollBack sont souvent utiliss
dans TRY EXCEPT:
TRY
IbTransaction1.StartTransaction;
dbite DUPOND
crdite MARTIN
IbTransaction1.Commit;
EXCEPT
IbTransaction1.RollBack;
END;

Nous ne pouvons appeler IbTransaction1.StartTransaction si la transaction


attache IbTransaction1 est ferme (committed ou Rolledback). Pour savoir si
une transaction est active, nous pouvons utiliser la fonction InTransaction. Ainsi:

IF NOT IbTransaction1.InTransaction
THEN IbTransaction1.StartTransaction;

Que se passerait-il si nous ralisons le traitement sans cette IbTransaction:

pendant l'excution de notre application d'autres applications (l'explorateur


de bases de donnes, un autre de nos exe) ne "verraient" pas la table
un autre IbQuery de notre propre application pourrait ne pas voir la table
lorsque l'EXE sera ferm, la transaction sera automatiquement ferme

Le plus simple est donc d'utiliser Commit.

4.5 - L'application
Pour crer notre table
crez une nouvelle application et appelez-la "ib_create_table"
placez un tIbDataBase sur la Forme. Cliquez deux fois sur IbDataBase1, renseignez
"local", "DataBase", "User Name" "Pass Word" et "Login Prompt":

Vrifiez la connection en basculant IbDataBase.Connected sur True, puis fermez la


connection (pour viter de monopoliser un utilisateur). La connection sera ouverte avant
l'envoi de la requte
placez un tIbTransaction sur la Forme, et reliez IbDatabase1.DefaultTransaction

placez un tIbQuery sur la tForme


slectionnez sa proprit DataBaseName et initialisez-la
IbDataBase1
placez un tButton sur la Forme et crez sa mthode OnClick. Placez-y les instructions de
cration:
procedure TForm1.create_table_Click(Sender: TObject);
begin
with IbQuery1 do
begin
Close;
with Sql do
begin
Clear;
Add('CREATE TABLE formations');
Add(' ('
+' f_numero INTEGER,');
Add(' f_nom CHAR(9),');
Add(' )');
end; // with Sql
Try
IbDatabase1.Open;
if ibtransaction1.InTransaction
then ibTransaction1.Commit;
ibtransaction1.StartTransaction;
ExecSql;
ibtransaction1.Commit;
except
on e: Exception do
display('pb_create '+ e.Message);
end; // try ... except
end; // with IbQuery1
end; // create_table_Click

compilez, excutez, et cliquez le bouton

Vous pouvez tlcharger le sources du projet "ib_create_table.zip".

4.6 - Vrifier la cration

Nous pouvons vrifier que la cration a t effectue en utilisant l'explorateur de


bases de donnes Delphi, ou en lisant les donnes ce cette table dans notre
application.
Pour lire les donnes il suffit d'envoyer la requte
SELECT * FROM formations

au moteur. Nous verrons cette requte SELECT en dtail plus bas, mais voici
comment procder pour notre test:
ajoutez un second tIbQuery sur la tForme
slectionnez sa proprit DataBaseName et initialisez-la
IbDataBase1
placez un second tButton sur la Forme et placez-y la requte de lecture:
procedure TForm1.select_Click(Sender: TObject);
begin
with IbQuery2 do
begin
Close;
with Sql do
begin
Clear;
Add('SELECT * FROM formations');
Try
Open;
display('ok');
except
On e: Exception do
display('not_there '+ e.Message);
end;
end; // with Sql
end; // with IbQuery2
end; // select_Click
compilez, excutez, et cliquez le bouton

4.7 - Effacer une table


Pour supprimer une Table, il faut excuter la requte:

DROP TABLE formations

Donc:
ajoutez un troisime tIbQuery sur la tForme
slectionnez sa proprit DataBaseName et initialisez-la
IbDataBase1
placez un autre tButton sur la Forme et placez-y la requte de suppression:
procedure TForm1.drop_table_Click(Sender: TObject);
begin
IbDatabase1.Open;
with IbQuery3 do
begin
Close;
with Sql do
begin
Clear;
Add('DROP TABLE formations');
end;
Try
if ibtransaction1.InTransaction
then ibTransaction1.Commit;
ibtransaction1.StartTransaction;
ExecSql;
ibtransaction1.Commit;
display(' ok');
except
on e: exception do
display('pb_drop '+ e.Message);
end;
end; // with IbQuery3
end; // drop_table_Click
compilez, excutez, et cliquez le bouton

Notez que:

nous avons utilis 3 IbQuery spars. Nous aurions aussi bien pu utiliser le
mme
nous aurions mme pu utiliser un IbSql. Pour ce tutorial que nous souhaitons
minimal, nous avons prfr un IbQuery qui permet la fois la modification

de donnes duServeur (CREATE, DROP) et la lecture de donnes depuis


le Serveur (SELECT)
notez toutefois, que pour envoyer une requte de modification il faut
utiliser tIbQuery.ExecSql, et pour la lecture c'est tIbQuery.Open.
tIbQuery.Open est quivalent tIbQuery1.Active:= True (mais pas
ExecSql). Comme l'Inspecteur propose Active (pour pouvoir positionner
ses lments visuels), nous pouvons ouvrir une Table depuis l'Inspecteur,
mais nous ne pouvons pas crer de Table en mode conception (il faut
excuter du code)

4.8 - Automatisation de la cration


Si nous avons plusieurs tables crer, le codage en dur se rvle trs vite
fastidieux. Or il est trs simple de paramtrer une procdure gnrique, et d'appeler
cette procdure en lisant les paramtres depuis un fichier.
Une solution est de placer sur disque les requtes SQL et de lire et excuter ces
requtes les unes aprs les autres.
Nous avons prfr utiliser une dfinition plus schmatique qui est analyse par la
procdure de lecture.
Voici notre schma que nous avons tap dans NotePad et plac dans le fichier
"schema_simple.txt":
formations
f_numero INTEGER
f_nom CHAR(23)
f_jours INTEGER
f_prix NUMERIC(12, 2)
dates
d_formation INTEGER
d_date DATE
d_ville INTEGER
villes
v_numero INTEGER
v_nom CHAR(30)

Nous avons simplement fourni

le nom de chaque table, la marge


le nom des champs et leur type Interbase, indent de deux espaces

On peut difficilement faire plus simple !


Notre programme va alors

lire le fichier
gnrer la requte pour crer chaque table

la cration de la table est ralise par une classe dont voici la dfinition:

Et:

type c_create_ib_table= class(c_basic_object)


m_table_name: String;
m_c_ib_query_ref: tIbQuery;
m_c_fields: tStringList;
m_exec_sql: Boolean;
Constructor create_ib_table(p_name, p_table_name: String;
p_c_ib_query: tIbQuery);
procedure drop_table;
procedure create_table;
procedure display_sql;
Destructor Destroy; Override;
end; // c_create_ib_table

la mthode de cration de la table est la suivante:


procedure c_create_ib_table.create_table;
var l_field_index: Integer;
begin
drop_table;
with m_c_ib_query_ref, Sql do
begin
display(m_table_name);
Clear;
Add('CREATE TABLE '+ m_table_name+ ' (');
for l_field_index:= 0 to m_c_fields.Count- 2 do
Add(m_c_fields[l_field_index]+ ',');
Add(m_c_fields[m_c_fields.Count- 1]+ ' )');
if m_exec_sql
then
try
if DataBase.DefaultTransaction.InTransaction
then DataBase.DefaultTransaction.Commit;

DataBase.DefaultTransaction.StartTransaction;
ExecSql;
DataBase.DefaultTransaction.Commit;
display(' ok '+ m_table_name);
except
on e: Exception do
display(' *** pb_create '+ e.Message);
end // try ... Except
else display('did_not_ask_to_create');
end; // with m_c_ib_query_ref, Sql
end; // create_table

cette mthode utilise la liste de dfinition des champs qui est charge partir
du fichier par la procdure suivante
procedure TForm1.create_script_Click(Sender: TObject);
var l_list_index: Integer;
l_line, l_trimmed_line: String;
l_c_create_ib_table: c_create_ib_table;
l_table_index: Integer;
begin
with tStringList.Create do
begin
LoadFromFile(k_script_path+ k_script_name);
l_c_create_ib_table:= Nil;
l_table_index:= 0;
for l_list_index:= 0 to Count- 1 do
begin
l_line:= Strings[l_list_index];
l_trimmed_line:= Trim(l_line);
if l_trimmed_line<> ''
then begin
if l_trimmed_line= l_line
then begin
if Assigned(l_c_create_ib_table)
then begin
l_c_create_ib_table.create_table;
l_c_create_ib_table.Free;
Inc(l_table_index);
end;
display_line;
l_c_create_ib_table:=
c_create_ib_table.create_ib_table('', l_line, IbQuery1);

end
else begin
l_c_create_ib_table.m_c_fields.Add(l_trimmed_line);
end;
end;
end; // for l_list_index
// -- the last table
if Assigned(l_c_create_ib_table)
then begin
l_c_create_ib_table.create_table;
l_c_create_ib_table.Free;
end;
Free;
end; // with tStringList
end; // create_script_Click

Notez que:

la procdure c_create_ib_table.create_table commence par appeler la


procdure c_create_ib_table.drop_table. Pour la premire cration, ceci est
inutile, mais si vous crez une seconde fois la base, il faut d'abord effacer la
table, sinon le Serveur refuse la cration.
mais dans le cas de la premire cration, DROP TABLE provoque aussi une
exception. C'est pourquoi cette instruction est place dans
un TRY ... EXCEPT. Cette exception va interrompre l'excution. Pour
poursuivre la cration:
o vous pouvez taper F9 pour passer outre
o vous pouvez placer Delphi en mode "ne pas s'arrter en cas
d'exceptions" en suprimant la coche de la CheckBox "stop on Delphi
Exceptions" situe dans:
Tools | Debugger | Language Exceptions
C'est le mode prfr lorsque l'on utilise beaucoup de bases de donnes,
compte tenu des nombreuses exceptions provoques tous les niveaux
(Serveur Sql, pilote, BDEou dBExpress, Ibx etc).

Vous pouvez tlcharger le source du projet "ib_create_tables_with_script.zip".

5 - Ajouter des Donnes


5.1 - Ajout simple
Ajoutons un enregistrement pour le stage
3, Interbase Delphi
L'instruction SQL est:
INSERT INTO formations
(f_numero, f_nom)
VALUES (3, 'Interbase Delphi')

L'ajout d'enregistrement va modifier les donnes du Serveur, donc nous


utiliserons tIbQuery.ExecSql.
De faon dtaille:
crez une nouvelle application et nommez-la "ib_insert_data"
placez un tIbTransaction sur la Forme
placez un tIbDataBase sur la Forme. Cliquez deux fois sur IbDataBase1, renseignez
"local", "DataBase", "User Name" "Pass Word" et "Login Prompt".
Vrifiez la connection en basculant IbDataBase1.Connected sur True, puis fermez la
connection.
Slectionnez DefaultTransaction et initialisez-la IbTransaction1
placez un tIbQuery sur la tForme
slectionnez sa proprit DataBaseName et initialisez-la IbDataBase1
placez un tButton sur la Forme et crez sa mthode OnClick. Placez-y les instructions
d'ajout:
procedure TForm1.insert_Click(Sender: TObject);
begin
IbDatabase1.Open;
with IbQuery1 do
begin
Close;
with Sql do
begin
Clear;
Add('INSERT INTO formations');

Add(' (f_numero, f_nom)');


Add(' VALUES (8, ''Delphi InterBase'')');
end; // with Sql
Try
if not IbTransaction1.InTransaction
then IbTransaction1.StartTransaction;
ExecSql;
IbTransaction1.Commit;
except
on e: Exception do
display('pb_insert '+ e.Message);
end; // Try...Except
end; // with IbQuery1
end; // insert_Click
compilez, excutez, et cliquez le bouton

Vous pouvez tlcharger les sources du projet "ib_insert_data.zip".

5.2 - Type CHARACTER


Pour spcifier les valeurs CHARACTER, Sql exige que la chane soit entoure de
guillemets. Suivant les Serveurs, il faut utiliser un guillemet simple ou double:
VALUES (3, 'Interbase Delphi')

ou
VALUES (3, "Interbase Delphi")

De plus si notre valeur est niche dans une String Pascal, il faut ddoubler les
guillemets
IbQuery1.Sql.Add(' VALUES (3, ''Interbase Delphi'')');

Pour simplifier cet imbroglio de guillemets, Delphi propose la mthode QuotedStr:


IbQuery1.Sql.Add(' VALUES (3, '+ QuotedStr('Interbase')+ ')');

5.3 - Type NUMERIC


Pour les valeurs numriques avec dcimales, nous devons batailler avec les points
et les virgules:

aux US, la partie entire est spare de la partie dcimale par un point:
3.1415
alors qu'en Europe nous disons
3,1415

les valeurs que nous tapons en PASCAL doivent donc respecter la syntaxe
US
mais si nous utilisons des primitives de conversion interface utilisateur
<=> Delphi, Delphi lit les paramtres de localisation dans la base de registre
Windows. Pour ma machine il est dit que le sparateur dcimal est la virgule.
Par consquent les fonctions telles que:
FloatToStr
attendent une virgule
Nous pouvons imposer le sparateur utiliser en spcifiant:
DecimalSeparator:= '.';

La rgle est donc simple:

les valeurs Delphi (Double ou autre) utilisent le point '.'


les composants visuels (tEdit etc) et les primitives de conversion
(FloatToStr) utilisent la virgule ','

5.4 - Type DATE


Un problme similaire intervient pour les dates:

Sql utilise le format US "anne mois jour". EXCLUSIVEMENT


suivant le type de primitive, Delphi utilisera un format US ou Europanis
(via le panneau de configuration Windows, ou des primitives de formatage)

le sparateur doit tre un slash / (et non pas un tiret ou autre)


2004/03/29

les dates doivent tre entoure de guillemets:


'2004/03/29'
car sinon Delphi effectue une division et calcule un Double, assimil
un tDateTime, et traduit en une date

En supposant que nous souhaitions fournir la date du 29 Mars 2004, nous pouvons
utiliser:
INSERT INTO dates
(d_numero, d_date)
VALUES (3, '2004/03/29')

et:
IbQuery1.Sql.Add('INSERT INTO dates');
IbQuery1.Sql.Add(' (d_numero, d_date');
IbQuery1.Sql.Add(' VALUES (3, ''2004/03/29'')');

5.5 - Automatisation de l'Ajout


Les valeurs insrer ont t figes dans notre code. Nous pouvons automatiser cet
ajout

soit par des scripts


soit par une procdure amplement paramtre
soit en mode interactif.

Nous allons prsenter ici l'utilisation d'une procdure.


En fait il n'y a rien de nouveau par rapport la technique ci-dessus, sauf que

la chane de la requte est construite en fonction de paramtres de la


procdure
la procdure appelante envoie les paramtres requis

Voici un exemple de procdure gnrique d'ajout:

procedure insert_generic(p_number: Integer; p_name: String; p_days: Integer; p_cost: Do


uble);
begin
with Form1, IbQuery1 do
begin
Close;
with Sql do
begin
Clear;
Add('INSERT INTO formations');
Add(' (f_numero, f_nom, f_jours, f_prix)');
DecimalSeparator:= '.';
Add(' VALUES ('+ IntToStr(p_number)+ ', '+ QuotedStr(p_name)
+ ', '+ IntToStr(p_days)+ ', '+ FloatToStr(p_cost)+ ')');
DecimalSeparator:= ',';
end;
Try
if not IbTransaction1.InTransaction
then IbTransaction1.StartTransaction;
ExecSql;
IbTransaction1.Commit;
display(' ok');
except
on e: Exception do
display(' pb_insert '+ e.Message);
end;
end; // with IbQuery1
end; // insert_generic

Et voici un exemple de procdure appelante


procedure TForm1.insert_batch_Click(Sender: TObject);
begin
IbDatabase1.Open;
insert_generic(1, 'Initiation Delphi', 3, 1400.40);
insert_generic(2, 'Bases de Donnes Delphi', 3, 1.400);
insert_generic(3, 'Interbase Delphi', 3, 1.400);
insert_generic(4, 'Composants Delphi', 3, 1.400);
insert_generic(5, 'UML Delphi', 3, 1.400);
insert_generic(4, 'Initiation Pascal', 4, 1.900);
end; // insert_batch_Click

Notez que:

la procdure appelante pourrait aussi bien lire ses donne d'une autre source
(un fichier FILE OF, un fichier ASCII ("comma separates values" ou autre),
un autre table (Oracle, Sql Serveur ou mme une autre table Interbase...)
le paramtres p_cost est de type Double:
o la procdure appelante envoie une valeur littrale avec un point
dcimal
insert_generic(2, 'Bases de Donnes Delphi', 3, 1.400);
o

la procdure appele utilise FloatToStr, qui attend une virgule


dcimale. Nous forons donc temporairement l'utilisation du point:
DecimalSeparator:= '.';
Add(' VALUES ('+ IntToStr(p_number)+ ... + FloatToStr(p_cost)+ ')');
DecimalSeparator:= ',';

6 - Lire et Afficher
6.1 - 5.1- Principe
Pour afficher un enregistrement, nous devons d'abord rcuprer ses valeurs du
Serveur.
Pour lire les donnes contenues dans une table, SQL utilise l'instruction SELECT.
Par exemple:
SELECT f_numero, f_nom
FROM formations

Lorsque le Serveur reoit cette requte:

il vrifie sa syntaxe
il construit une table contenant les valeurs demandes
ces donnes sont envoyes au Client

Plus concrtement:
crez une nouvelle application et appelez-la "ib_select"

placez un tIbTransaction sur la Forme


placez un tIbDataBase sur la Forme. Cliquez deux fois sur IbDataBase1, renseignez
"local", "DataBase", "User Name" "Pass Word" et "Login Prompt".
Vrifiez la connection en basculant IbDataBase1.Connected sur True, puis fermez la
connection.
Slectionnez DefaultTransaction et initialisez-la IbTransaction1
placez un tIbQuery sur la tForme
slectionnez sa proprit DataBaseName et initialisez-la IbDataBase1
placez un tButton sur la Forme et crez sa mthode OnClick. Placez-y les instructions de
lecture:
procedure TForm1.select_Click(Sender: TObject);
begin
IbDataBase1.Open;
with IbQuery1 do
begin
Close;
with Sql do
begin
Clear;
Add('Select * from formations');
Open;
end; // with Sql
end; // with IbQuery1
end; // select_Click
compilez, excutez, et cliquez le bouton

Vous pouvez tlcharger le source du projet "ib_select.zip".

6.2 - Comment a Marche


Lorsque nous excutons IbQuery1.Open:

Delphi envoie la requte SELECT au Serveur


celui-ci construit une table rsultat, en utilisant, bien sr, la table
FORMATIONS, mais aussi ventuellement des tables annexes (index,
contraintes etc.).

cette table rsultat est envoye via le rseau au Client Interbase. Celui ci la
transmet ensuite l'application. Delphi place alors ce rsultat dans un
tampon mmoire associ tIbQuery:

6.3 - Affichage
Une fois que IbQuery a rcupr les donnes nous pouvons les afficher ou les
traiter en mmoire.
Pour afficher les donnes nous utilisons:

un tDataSource qui rcupre les donnes du tIbQuery

un ou plusieurs composants d'affichage reli au tDataSource. Par exemple


un tDbGrid.

Par consquent:
slectionnez dans la page "Data Access" de la Palette le composant tDataSource:

et placez le sur la Forme


slectionnez dans la page "Data Controls" de la Palette le composant tdbGrid:

et placez le sur la Forme


slectionnez sa proprit DataSource et donnez-lui la valeur DataSource1
compilez, excutez, et cliquez le bouton qui lance la requte

6.4 - Affichage en mode conception


Nous pouvons aussi lancer la requte de lecture en mode conception. Il faut pour
cela que:

tIbDatabase soit correctement initialis


tIbQuery soit reli tIbDatabase
la proprit tIbQuery.Sql soit initialise en utilisant l'Inspecteur
la requte soit envoye en basculant tIbQuery.Active sur True

Donc:
slectionnez IbQuery1
cliquez sur sa proprit Sql
Delphi ouvre l'diteur de Sql. A prsent les tables cres sont affiches:

slectionnez la table "formation"


les colonnes de cette table sont affiches
slectionnez les colonnes que vous souhaitez afficher (ou "*")
fermez l'diteur de requte
slectionnez Active, et basculez sa valeur sur True
Delphi affiche les valeurs de la table

6.5 - SELECT
SELECT est la seule instruction de lecture de donnes du Serveur. Sa structure
gnrale est:
SELECT colonnes
FROM tables
WHERE conditions

et:

colonnes indique quelles colonnes nous voulons voir figurer dans le rsultat.
Nous pouvons
o citer explicitement les colonnes souhaites, dans l'ordre qui nous
convient:
SELECT f_nom, f_numero

FROM formations
o

Nous pouvons ne demander qu'une partie des colonnes (projection)

SELECT f_nom
FROM formations
o

L'abrviation "*" permet de dsigner "toutes les colonnes"

SELECT *
FROM formations
o

effectuer des sommes, des moyennes (agrgats)

SELECT COUNT(*)
FROM formations

le rsultat est alors une table d'une ligne, une colonne avec le nombre
de formations
tables contient le noms des tables utiliser pour calculer le rsultat. Par
exemple:
o

SELECT *
FROM formations, dates
WHERE f_nom='UML ET DELPHI'

conditions permet
o de ne retenir que certaines lignes du rsultat. Par exemple
SELECT *
FROM dates
WHERE f_date> '2004/05/01'
o

de trier le rsultat:

SELECT *
FROM dates
ORDER BY f_date
o

Ce qu'il faut bien comprendre est que:

SELECT calcule un rsultat A PARTIR de tables, mais que le rsultat n'EST


PAS les tables. Lorsque nous demandons une somme ou une moyenne, par
exemple,SELECT retourne un nombre.
Certes, "SELECT * FROM formations" retourne bien les mmes donnes
que celle de la table sur disque "formations". Mais ce n'est qu'un instantan
ralis lorsque la requte a t lance. Il s'agit d'une copie. Si un autre
utilisateur ajoute une nouvelle formation la copie que nous avons dans le
cache attach IbQuery ne le reflte pas
pour rafrachir notre cache, la SEULE faon de faire est de relancer la
requte (en fermant et rouvrant tIbQuery)

6.6 - Changement de requte


Notre exemple prsente une lecture dans laquelle l'utilisateur tape sa requte dans
un tEdit. Rien de bien particulier, sauf que ceci permet d'explorer facilement une
table (sans utiliser l'Explorateur de bases de donnes).

6.7 - Requte paramtre


Chaque fois que nous souhaitons une information, nous devons excuter
un SELECT.
Lorsque la requte est complexe (plusieurs tables, sous-requte, agrgats etc)
le Serveur essaye d'optimiser le calcul du rsultat. Cette optimisation peut tre
longue.
Si nous devons excuter la requte plusieurs fois de suite, Sql offre la possibilit de
scinder la requte en deux:

envoyer le squelette de la requte contenant des paramtres


fournir la valeur des paramtres et demander le rsultat

Dans le premier temps, le Serveur calcule sa mthode d'accs, mais n'excute rien
car il lui manque la valeur des paramtres. Lorsque le Serveur reoit les paramtres,
il lance le calcul en utilisant son plan d'valuation, et retourne le rsultat. Et ceci
autant de fois que le squelette de la requte ne change pas.
Pour effectuer ce travail en deux temps:

nous rdigeons une requte qui contient des paramtres. En Delphi, ces
paramtres sont dsigns par un identificateur prcd de ":". Par exemple
SELECT *
FROM dates
WHERE f_date= : ma_date

Notez que:
o l'identificateur n'a pas besoin d'avoir le mme nom que la colonne
o les paramtres peuvent seulement apparatre dans la
clause WHERE (pas dans les champs ou pour le nom des tables)
o ":" et ma_date sont colls (pas d'espace entre les deux)
la requte est envoye vers le Serveur par
IbQuery1.Prepare;

lorsque nous souhaitons obtenir un rsultat


o nous initialisons la valeur des paramtres en
utilisant tIbQuery.Params
IbQuery1.Params[0].AsString:= '2004/03/01';
o

ou
IbQuery1.ParamByName('ma_date').AsString:= Edit3.Text;

nous lanons la requte par:


IbQuery1.Open;

En pratique:
crez une nouvelle application et appelez-la "p_ib_parametrized"
placez un tIbTransaction sur la Forme
placez un tIbDataBase sur la Forme. Cliquez deux fois sur IbDataBase1, renseignez
"local", "DataBase", "User Name" "Pass Word" et "Login Prompt".
Vrifiez la connection en basculant IbDataBase1.Connected sur True, puis fermez la

connection.
Slectionnez DefaultTransaction et initialisez-la IbTransaction1
placez un tIbQuery sur la tForme
slectionnez sa proprit DataBaseName et initialisez-la IbDataBase1
placez un tButton sur la Forme et crez sa mthode OnClick. Placez-y les instructions de
prparation:
procedure TForm1.prepare_Click(Sender: TObject);
begin
with IbQuery1 do
begin
Close;
with Sql do
begin
Clear;
Add('SELECT * FROM formations WHERE f_nom= :le_nom');
end; // with Sql
Prepare;
end; // with IbQuery1
end; // prepare_Click
placez un tButton sur la Forme et crez sa mthode OnClick. Placez-y les instructions qui
initialisent les paramtres et ouvrent la table:
procedure TForm1.close_params_open_Click(Sender: TObject);
begin
with IbQuery1 do
begin
// -- close in case has done a previous request
Close;
// -- set the parameter values
with Sql do
ParamByName('le_nom').AsString:= parameter_edit_.Text;
// -- send the parameter to the database
Open;
end; // with IbQuery1
end; // close_params_open_Click
compilez, excutez, et cliquez le bouton qui lance la requte

Vous pouvez tlcharger le source du projet "ib_parametrized.zip".

7 - Modifier des Donnes


7.1 - Principe
Supposons que notre table FORMATIONS contienne les valeurs suivantes:

Pour changer le libell "Interbase Delphi" en "Interbase" nous utilisons une


requte UPDATE:
UPDATE formations
SET f_nom= 'Interbase'
WHERE f_numero= 8

Cette requte qui modifie les donnes du Serveur est envoye vers le Serveur en
utilisant tIbQuery1.ExecSql

En dtail:
crez une nouvelle application "p_update_data"
placez un tIbTransaction sur la Forme
placez un tIbDataBase sur la Forme. Cliquez deux fois sur IbDataBase1, renseignez
"local", "DataBase", "User Name" "Pass Word" et "Login Prompt".
Vrifiez la connection en basculant IbDataBase1.Connected sur True, puis fermez la
connection.
Slectionnez DefaultTransaction et initialisez-la IbTransaction1
placez un tIbQuery sur la tForme
slectionnez sa proprit DataBaseName et initialisez-la IbDataBase1
placez un tButton sur la Forme et crez sa mthode OnClick. Placez-y les instructions de
modification:
procedure TForm1.Button1Click(Sender: TObject);
begin

with IbQuery1 do
begin
Close;
with Sql do
begin
Clear;
Add('UPDATE formations SET f_nom=''Interbase'' where f_numero= 3');
end; // with Sql
Try
ExecSql;
except
on e:exception do
display(' *** pb_update'+ e.Message );
end; // Try...Except
end; // with IbQuery1
end; // Button1Click
compilez, excutez, et cliquez le bouton

Vous pouvez tlcharge le source du projet ib_update_data.zip".

7.2 - La clause WHERE


Dans l'instruction prcdente nous avons spcifi la valeur f_numero l'aide
de WHERE.
Que se passerait-il si nous envoyons:
UPDATE formations
SET f_nom= 'Interbase'

Eh bien le Serveur modifierait le nom de TOUS les enregistrements.


Si nous souhaitons limiter les modifications certaines lignes, il est impratif:

que chaque ligne de chaque table ait un identificateur unique pour que nous
puissions dsigner cette ligne en cas de modification
que nous indiquions dans UPDATE les lignes modifier

Si en revanche nous souhaitons modifier plusieurs lignes la fois, nous pouvons :

omettre WHERE. Par exemple, pour passer tous les noms en majuscule:
UPDATE formations
SET f_nom= UPPER(f_nom)

changer plusieurs lignes satisfaisant une condition. Pour effectuer une


rduction promotionnelle de 5% sur les prix infrieurs 1400 euros :
UPDATE formations
SET f_prix= f_prix* 0.95
WHERE WHERE f_prix<= 1400.00

Toutefois:
o ne rptez pas cette requte de rduction trop souvent: il faut bien que
je mange un peu !
o nous faisons des conditions tarifaires particulires (groupes,
formations intra, tudiant, particulier, rinsertion...), que nous sommes
prts vous prsenter si vous me tlphonez directement l'Institut
Pascal au 01.42.83.69.36.

Il est donc fondamental de bien comprendre qu'un UPDATE sans


clause WHERE porte sur tous les enregistrements.

7.3 - UPDATE et AFFICHAGE


Ce fonctionnement de UPDATE est particulirement important lorsque nous
modifions un enregistrement affich l'cran dans un dbGrid: nous pensons
que Interbase sait que nous "modifions l'enregistrement courant". Or ce concept
d'enregistrement courant n'existe pas en mode SQL:

le dbGrid affiche le rsultat obtenu par une requte SELECT


or SELECT calcule un rsultat "instantan". Nous avons vu
que SELECT peut supprimer des colonnes (projection), retirer des lignes
(filtrage), effectuer des moyennes ou calculer un rsultat partir de plusieurs
tables.
Le rsultat du calcul est envoy par le Serveur vers le Client, et le Serveur ne
garde plus aucune trace de ce qu'il a envoy. Un autre utilisateur peut mme
modifier les tables sans que notre Client en soit inform

par consquent toute modification du rsultat ne pourait tre que local.

en fait par dfaut le rsultat est non-modifiable (ReadOnly). Et si nous


essayons de taper une nouvelle valeur au clavier, Delphi refuse de modifier
le champ du dbGrid.

La seule faon de modifier une valeur affiche de faon interactive est de


dclencher une requte UPDATE en utilisant un composant tIbUpdateSql. Le plus
simple pour synchroniser les modifications est d'utiliser un
composant tIbUpdateSql.

7.4 - tIbUpdateSql
Ce composant a pour vocation de contenir la requte SQL qui doit tre excute
lorsque le tIbQuery doit tre modifi. Cette requte est place
danstIbUpdaateSql.ModifySql.
Automatisons par exemple la mise jour de f_nom lorsque nous tapons une valeur
dans un tDbGrid:
crez une nouvelle application et appelez-la "p_update_sql"
placez un tIbTransaction sur la Forme
placez un tIbDataBase sur la Forme. Cliquez deux fois sur IbDataBase1, renseignez
"local", "DataBase", "User Name" "Pass Word" et "Login Prompt".
Vrifiez la connection en basculant IbDataBase1.Connected sur True, puis fermez la
connection.
Slectionnez DefaultTransaction et initialisez-la IbTransaction1
placez un tIbQuery sur la tForme

slectionnez sa proprit DataBaseName et initialisez-la IbDataBase1


slectionnez sa proprit Sql et placez-y la requte
SELECT *
FROM formations

ajoutez un composant tDataSource

slectionnez DataSet et donnez-lui la valeur IbQuery1

reliez DataSource1 un tDbGrid:

placez un tdbGrid de sur la Forme

slectionnez sa proprit DataSource et reliez la DataSource1

ouvrez IbQuery1 en basculant Active sur True


ajoutez un tIbUpdateSql:

slectionnez la page "Interbase" de la Palette, slectionnez le


composant tIbUpdateSql

et posez-le sur la Forme

connectez le IbQuery1 et IbUpdateSql:


o en initialisant IbQuery1.UpdateObject avec IbUpdateSql1
o en basculant IbQuery1.CachedUpdate True
slectionnez sa proprit IbUpdateSql1.ModifySql, cliquez sur l'ellipse pour ouvrir
l'diteur de ModifySql (c'est un diteur de tStrings classique):

tapez-y la requte de modification


UPDATE formations
SET f_nom= :f_nom
WHERE f_numero= :f_numero

compilez, excutez:

Slectionnez dbGrid1, tapez une valeur dans la colonne F_NOM.


Notez bien que le dbGrid permet prsent la modification de valeurs (alors que
sans tIbUpdateSql ces modifications taient impossibles).
lorsque nous quittons la ligne (en changeant de composant, ou en changeant de
ligne dans le dbGrid), la requte de modification est envoye vers le Serveur

Notez que:

pour dsigner les valeur des champs utiliser, nous avons employ une
requte paramtre, en dsignant les paramtres par le nom du champ:
f_nom= :f_nom

nous aurions pu modifier plusieurs colonnes


UPDATE formations
SET f_nom= :f_nom, f_jours:= :f_jour, f_prix= :f_prix
WHERE f_numero= :f_numero

Vous pouvez tlcharger le projet "ib_update_sql.zip".

7.5 - "Old" values


Nous avons utilis f_numero pour identifier la ligne modifier. Mais que se passet-il si nous souhaitons modifier la cl elle-mme ? En effet :f_numero dsigne la
valeur actuelle, qui est la valeur modifie, et non pas la valeur qui permet de
dsigner la ligne dans la table.
Pour rsoudre ce problme, Delphi a cre le concept des "old_values",
correspondant la valeur que le champ avait lors de la lecture de la table. Et le
paramtre pour dsigner la valeur se note :old_f_numero

Si nous souhaitons pouvoir modifier tous les enregistrements, nous utilisons donc
dans tIbUpdateSql.ModifySql une requte du type:
UPDATE formations
SET f_numero= :f_numero, f_nom= :f_nom
WHERE f_numero= :old_f_numero

Ce concept de "old values" est fondamental pour la mise jour concurrente que
nous prsenterons voir ci-dessous.

7.6 - Insertion, Effacement


Notez que:

tIbUpdateSql possde aussi des proprits InsertSql et DeleteSql qui peuvent


contenir les instructions xcuter lorsque nous ajoutons un nouvel
enregistrement (en ajoutant une ligne au bas du tDbGrid), ou que nous en
effaons un (par Ctlr Suppr sur une ligne du tDbGrid)
le composant tIbUpdateSql possde un diteur de composant permettant un
criture plus automatique des requtes SQL. Cliquez deux fois
sur IbUpdateSql1 et Delphiprsentera:

8 - Effacer de Donnes
8.1 - Principe de l'effacement
Pour effacer l'enregistrement ayant le numro 8, nous excutons une requte
DELETE
FROM formations
WHERE f_numero= 3

En dtail:

crez une nouvelle application "p_delete_data"


placez un tIbTransaction sur la Forme
placez un tIbDataBase sur la Forme. Cliquez deux fois sur IbDataBase1,
renseignez "local", "DataBase", "User Name" "Pass Word" et "Login
Prompt".
Vrifiez la connection en basculant IbDataBase1.Connected sur True, puis
fermez la connection.
Slectionnez DefaultTransaction et initialisez-la IbTransaction1
placez un tIbQuery sur la tForme
o slectionnez sa proprit DataBaseName et initialisez-la
IbDataBase1
placez un tButton sur la Forme et crez sa mthode OnClick. Placez-y les
instructions de modification:
procedure TForm1.delete_edit_Click(Sender: TObject);
begin
with IbQuery1 do
begin
Close;
with Sql do
begin
Clear;
Add(DELETE FROM formations WHERE f_numero= 3);
end; // with Sql
Try
ExecSql;
except
display('< *** pb_update');
end; // try...except
end; // with IbQuery1
end; // update_edit_Click

compilez, excutez, et cliquez le bouton

Vous pouvez tlcharger le source du projet ib_delete_data.zip".

De faon naturelle:

comme pour UPDATE, il faut veiller spcifier les enregistrements effacer


dans WHERE. Une requte sans WHERE peut effacer toutes les lignes. Et
sans aucun "undelete". A bon entendeur...
nous pouvons utiliser un tIbUpdateSql en plaant
dans tIbUpdateSql.DeleteSql la requte du type:
DELETE
FROM formations
WHERE f_numero= :old_f_numero

9 - Plusieurs Tables
9.1 - La Normalisation des Tables
Lorsque nous crons une application, nous rpartissons les donnes dans des tables:
les factures, les articles, les adresses des fournisseurs etc.
Cette activit de rpartition peut devenir trs complexe. Imaginez la modlisation
de l'activit de Renault ou des Galeries Farfouillettes: faut-il placer l'adresse des
fournisseurs avec les pices du stock, ou crer un table spare, combien de tables
faut-il alors crer etc.
Pour des projets plus modestes, la rpartition est souvent intuitive, et se rsume
souvent adapter des structures utilises dans d'autres projets.
Nanmoins, les donnes utilises par SQL doivent obir un nombre minimal de
contraintes:

nombre de colonnes

redondance / dpendances

Prenons le cas simple de formations dont le calendrier est le suivant (le vrai
calendrier se trouve <%dates_des_formations>):

Initiation Delphi:

Mars
2 au 4 Metz

Delphi Interbase:
Delphi UML:

Avril

6 au 8 - Paris
23 au 25 Paris

20 au 22 Chlons

Mai

4 au 6 Toulouse
25 au 27 Lyon

9.1.1 - Mme nombre de colonnes

La premire exigence est simple: chaque enregistrement de chaque table


relationnelle doivent avoir le mme nombre de colonnes.
Dans notre cas, toutes les formations n'ont pas ncessairement le mme nombre de
sessions. La premire exigence SQL n'est pas respecte: le premier enregistrement
aurait 2 colonnes, le second 3 etc.
La solution est simple: il suffit de crer un enregistrement spar pour chaque date.

9.1.2 - Non redondance - Anomalie de mise jour

Supposons que nous souhaitions modifier le nom "Interbase Delphi" en "Delphi


Interbase". Il faudra veiller modifier tous les enregistrements contenant cette
valeur. Le risque est donc des modifications partielles ou incohrente (anomalie de
mise jour).
Le plus simple est alors de placer les valeurs mentionnes plusieurs endroits dans
une table spare avec un code, et d'utiliser ce code pour rfrencer la valeur
commune dans les autres tables.
Dans notre cas:

nous crons une table avec un code (arbitraire) de formation et le nom de la


formation:

nous crons une table avec les dates et les villes, en plaant le code de la
formation pour dsigner le nom des formations

9.1.3 - Anomalie de suppression

D'autre part, si le stage de Mars a eu lieu, nous effaons la ligne "1, 2004/03/02,
Metz". Ceci supprime aussi le fait que nous ralisons des stages Metz.
Le fait que la suppression d'une ligne fasse disparatre une information est appel
"anomalie de suppression".
Si nous souhaitons prsenter les villes o les stages peuvent avoir lieu,
indpendamment de sessions programmes dans les trois prochains mois, il faut
sparer les villes dans une table diffrente:

une table avec les villes possibles:

une table avec les dates et le lieu:

D'o la configuration finale:

les types de formations:

les villes o les sessions peuvent avoir lieu

les types de stage, dates et le lieu:

9.2 - Jointure
Ayant rparti les donnes dans plusieurs tables pour rpondre aux exigences SQL
et au souci de non-redondance / absence d'anomalies, nous avons prsent
l'obligation de recoller les morceaux pour reconstituer l'information de dpart: pour
savoir o et quand se droulent les formations "Interbase" nous devons:

cherche le code de la formation "Interbase" dans la table "formations": c'est


le code 3
trouver les dates et lieux dans la table "dates": (3, '2004/04/06', 101) et (3,
'2004/05/04', 102)
finalement le nom de la ville est trouv dans la table "ville": 101 c'est Paris
et 102 Toulouse

La solution standard propose par SQL est la jointure: nous effectuons


un SELECT en demandant de reconstituer une table en ajoutant les colonnes
provenant de plusieurs tables:

pour un premier essai, nous mlangeons les deux tables "formations" et


"dates":
SELECT *
FROM formations, dates

nous obtenons:

Le Serveur retourne toutes les combinaisons de valeurs de la premire table


avec toutes les valeurs de la seconde. Il faut limiter le rsultat aux lignes
o f_numero est gal d_formation (mme code de stage)

voici le second essai:


SELECT *
FROM formations, dates
WHERE f_numero= d_formation

le rsultat est:

maintenant que les lignes sont correctement associes, il est inutile d'afficher
deux fois le numro du stage. Nous pouve effectuer une projection en
supprimant la colonned_formation du rsultat:
SELECT f_numero, f_nom, d_date, d_ville
FROM formations, dates
WHERE f_numero= d_formation

comme nous ne nous intressons qu' Interbase, il faut liminer les lignes ne
concernant pas interbase (filtrage) en ajoutant une condition WHERE:
SELECT f_numero, f_nom, d_date, d_ville
FROM formations, dates
WHERE f_numero= d_formation AND f_nom= 'Interbase Delphi'

finalement pour voir le nom de la ville, il faut faire entrer la troisime table
dans la danse:
SELECT f_numero, f_nom, d_date, v_nom
FROM formations, dates
WHERE f_numero= d_formation
AND d_ville= v_numero
AND f_nom= 'Interbase Delphi'

Notez toutefois que:

le rsultat n'est pas trs satisfaisant: le nom du stage est rpt chaque ligne
le calcul est de plus inefficace: le Serveur ne peut nous retourner le rsultat
que lorsque tout le calcul est ralis.

L'exemple de la jointure peut tre tlcharg ici "ib_join.zip".

9.3 - Relation Matre Dtail


Pour viter le calcul massif de la jointure, Delphi propose d'utiliser plutt le
mcanisme Matre / Dtail.
Le principe est le suivant:

nous slectionnons un enregistrement de la table Matre "formations"


cet enregistrement contient le numro de formation, qui permet de retrouver
dans la table Dtail "dates" les dates pour ces formations
il suffit alors de lancer un SELECT sur ces dates, pour rcuprer les dates de
ces formations

9.3.1 - Matre Dtail manuel

Nous pouvons provoquer le calcul Matre Dtail chaque fois que nous dtectons
une modification d'enregistrement dans la table Matre: c'est
l'vnementDataSourceMatre.OnDataChange qui nous signale ce changement.
Par consquent:
Par consquent:
crez une nouvelle application et appelez-la "p_master_detail"
placez un tIbTransaction sur la Forme
placez un tIbDataBase sur la Forme. Cliquez deux fois sur IbDataBase1, renseignez
"local", "DataBase", "User Name" "Pass Word" et "Login Prompt".

Vrifiez la connection en basculant IbDataBase1.Connected sur True, puis fermez la


connection.
Slectionnez DefaultTransaction et initialisez-la IbTransaction1
pour le Matre:

placez un tIbQuery sur la tForme


o slectionnez sa proprit DataBaseName et initialisez-la IbDataBase1
o slectionnez sa proprit Sql, ouvrez l'diteur, et tapez:
SELECT f_numero, f_nom
FROM formations
o

slectionnez Active et basculez sa valeur sur True


placez un tDataSource sur la Forme
o slectionnez DataSet et initialisez sa valeur IbQuery1
placez un tdbGrid sur la Forme
o slectionnez DataSource et initialisez sa valeur DataSource1
les donnes de la table "formation" apparaissent

pour le Dtail:

placez un second tIbQuery sur la tForme


o slectionnez sa proprit DataBaseName et initialisez-la IbDataBase1
placez un tDataSource sur la Forme
o slectionnez DataSet et initialisez sa valeur IbQuery2
placez un tdbGrid sur la Forme
o slectionnez DataSource et initialisez sa valeur DataSource2

provoquez la slection de Dtail en fonction du Matre

slectionnez DataSource1 (celui du MAITRE)


crez son vnement OnDataChange, et tapez-y la requte
procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TFie
ld);
// -- when the MASTER datasource changes, select the details
begin
with IbQuery2 do
begin
Close;
with Sql do
begin
Clear;
Add('SELECT d_formation, d_date'
+ ' FROM dates'
+ ' WHERE d_formation= '+ IbQuery1.FieldByName('f_numero').A

sString);
end; // with Sql
Open;
end; // with IbQuery1
end; // TForm1.DataSource1DataChange

petite prcaution: lorsque la Forme sera cre, IbQuery1 sera ouverte, les lignes
remplies, et DataSource1.OnDataChange appel. Il est donc impratif
queIbQuery2 existe AVANT IbQuery1. Pour vous assurez que c'est le cas,
slectionnez dans le menu "Edit" le sous-menu "Creation Order" et dplacez les
composants pour que cette contrainte soit respecte:

compilez, excutez, et cliquez le bouton

Vous pouvez tlcharger ce projet "ib_master_detail.zip".

9.3.2 - Utilisation de tQuery.DataSource

Nous pouvons demander Delphi de raliser le SELECT du dtail


automatiquement en initialisant la proprit tIbQueryDetail.DataSource du Dtail
vers DataSourceMatre:
crez une nouvelle application et appelez-la "p_master_detail_automatic"
placez un tIbTransaction sur la Forme
placez un tIbDataBase sur la Forme. Cliquez deux fois sur IbDataBase1, renseignez
"local", "DataBase", "User Name" "Pass Word" et "Login Prompt".

Vrifiez la connection en basculant IbDataBase1.Connected sur True, puis fermez la


connection.
Slectionnez DefaultTransaction et initialisez-la IbTransaction1
pour le Matre:

placez un tIbQuery sur la tForme


o slectionnez sa proprit DataBaseName et initialisez-la IbDataBase1
o slectionnez sa proprit Sql, ouvrez l'diteur, et tapez:
SELECT f_numero, f_nom
FROM formations
o

slectionnez Active et basculez sa valeur sur True


placez un tDataSource sur la Forme
o slectionnez DataSet et initialisez sa valeur IbQuery1
placez un tdbGrid sur la Forme
o slectionnez DataSource et initialisez sa valeur DataSource1
les donnes de la table "formation" apparaissent

pour le Dtail:

placez un second tIbQuery sur la tForme


o slectionnez sa proprit DataBaseName et initialisez-la IbDataBase1
placez un tDataSource sur la Forme
o slectionnez DataSet et initialisez sa valeur IbQuery2
placez un tdbGrid sur la Forme
o slectionnez DataSource et initialisez sa valeur DataSource2

provoquez la slection de Dtail en fonction du Matre

slectionnez IbQuery2 (le DETAIL)


slectionnez sa proprit DataSource et initialisez la
DataSource1 (le tDatasource du MAITRE)
slectionnez sa proprit Sql, ouvrez l'diteur, et tapez y la requte de slection:
SELECT d_formation, d_date
FROM dates
WHERE d_formation= :f_numero

Notez que:

slectionnez Active et basculez sa valeur sur True


vous verrez les deux table, et en mode conception

c'est le tQuery du DETAIL qui dsigne le tDataSource du Matre, et non


l'inverse. Dans tout Delphi, ds que nous souhaitons associer 1 composants
n autres composants, la liaisons est toujours ralise au niveau de
l'Inspecteur d'Objets de n vers 1.
il arrive que par mgarde nous fassions pointer la
proprit DataSource d'un tDataSet vers son tDataSource associ, ce qui est
une erreur. Peut tre aurait-on pu appeler cette
proprit MasterSource (comme cela est fait d'autres endroits)
notez que le Dtail rcupre la valeur du Matre par une requte paramtre:
SELECT d_formation, d_date
FROM dates
WHERE d_formation= :f_numero

Notez aussi que le paramtre :f_numero a pour nom le NOM DU CHAMP


du matre (alors qu'auparavant le nom du paramtre pouvait tre arbitraire).
C'est le fait que ce nom dsigne la colonne de jointure qui vite
l'initialisation par Param_xxx

Paradox via ADO - John COLIBRI.

rsum : comment grer des tables Paradox en utilisant les


composants d'accs ADO de Delphi
mots cl : Paradox - ADO - tAdoConnection - tAdoDataSet
logiciel utilis : Windows XP personnel, Delphi 6.0, Paradox 7
matriel utilis : Pentium 2.800 Mhz, 512 Meg de mmoire, 250
Giga disque dur
champ d'application : Delphi 1 5, Delphi 6, Delphi 7, Delphi 2006,
Turbo Delphi, Delphi 2007 sur Windows
niveau : dveloppeur Delphi
plan :
o Paradox Via ADO
o Connexion Paradox ADO
o Perspectives ADO et Paradox
o Tlcharger le code source du projet Delphi

1 - Paradox Via ADO


Un de nos clients utilise Paradox avec le BDE (Borland Data Engine), et souhaite
se connecter en utilisant la suite de composants ADO. Cet article prsente
simplement comment tablir ce type de connexion.

2 - Connexion Paradox ADO


2.1 - Connexion par le BDE
La faon traditionnelle de se connecter Paradox est d'utiliser le BDE:

Soit:

optionnellement un tDatabase explicite, et une tSession


un tQuery pour les requtes SQL, ou un tTable pour le portage depuis dBase
pour la partie visuelle, des tDataSources et des composants
visuels: tDbGrid, tDbEdit etc

2.2 - L'Architecture ADO


ADO a t conu pour pouvoir accder tout type de source de donnes, allant du
fichier .TXT ou .HTML aux bases de donnes rparties Oracle.
Vaste programme ! Et ceci explique la complexit des composants qui constituent
la couche ADO. En tout une cinquantaine de composants.

Heureusement Delphi a regroup ces composants en simplifiant l'interface que nous


pouvons utiliser.
Schmatiquement cela ressemble ceci:

Soit:

un tAdoConnection qui permet de se relier aux donnes traiter, et


permettant aussi de grer les transactions
un tAdoCommand, avec ou sans tParameters, contenant en gnral la
requte Sql
un tAdoDataSet, qui a les fonctionalits du tDataSet (Open, Close, Next etc),
ainsi qu'une proprit Connection et Command
partir de l ce sont les composants
traditionnels: tDataSources et tDbGrid, tDbEdit etc

De faon plus prcises, voici un diagramme de classe UML reprsentant


l'ensemble:

2.3 - La chane de connexion ADO


Le plus difficile pour utiliser ADO est de fournir la chane de connexion. Celle ci
contient naturellement les paramtres pour dsigner la source de donnes:

adresse IP si ncessaire
chemin et ventuellement nom du fichier contenant les donnes
peut tre des noms d'utilisateurs et des mots de passe

Beaucoup de "si" et de "mais", qui proviennent naturellement des nombreuses


sources de donnes trs diffrentes les unes des autres.
Par consquent impossible de taper directement la valeur
de tAdoConnection.ConnectionString. Delphi propose bien un Wizard pour faciliter
la construction de la chane. Mais malgr tout, il faut encore fournir des paramtres
que personne ne peut inventer, mme en faisant preuve de beaucoup d'imagination.
Pour nous tirer d'affaire nous pouvons utiliser:

soit des projets de dmonstration trouvs ici ou l


soit Google, o il existe des pages de "connectionstrings"
soit les newsgroups ou quelque bon samaritain voudra bien vous fournir la
chane magique.

2.4 - Connection Paradox ADO


Voici donc comment procder pour Paradox
copiez (pour effectuer le test) quelques Tables Paradox (extensions .DB, par exemple
CUSTOMER.DB) situes dans le rpertoire des dmonstrations Delphi dans un
rpertoire pour votre essai:
C:\Program Files\Fichiers communs\Borland Shared\Data\
crez une nouvelle application Delphi
de l'onglet ADO de la Palette slectionnez tAdoConnection:

dans l'Inspecteur d'Objet, slectionnez ConnectionString et cliquez sur l'ellipse ...


le dialogue de construction de la chane de connexion est affich:

cliquez "Build..."
la liste des fournisseurs est prsente:

slectionnez "Microsoft Jet 4.0 Ole Db Provider". Le mot important est JET qui est le
nom gnrique du moteur d'accs
cliquez "Suivant >>"
l'onglet "Connexion" est affich

dans "1 slectionnez le nom de base de donnes", fournissez le chemin des tables
Paradox. Dans notre cas
C:\programs\fr\bdd\paradox_via_ado\_data\
puis, pour Paradox:

slectionnez l'onglet "Toutes"


puis la ligne "Extended Properties ..."
puis cliquez "Modifier la valeur"

le dialogue de saisie des types de moteurs Sql est prsent:

pour Paradox, tapez:

Paradox 7.x
et cliquez "Ok"
retournez dans l'onglet "Connexion"
la valeur Paradox a bien t entre
cliquez "Tester la connection"

cliquez "Ok", "Ok", "Ok"

Pour info, la valeur de la ConnectionString ainsi construite a la valeur suivante (en


une seule ligne):
Provider=Microsoft.Jet.OLEDB.4.0;
Data Source=C:\programs\fr\bdd\paradox_via_ado\_data;
Extended Properties=Paradox 7.x;
Persist Security Info=False

2.5 - Afficher des donnes


Pour vrifier notre connextion:
dans l'Inspecteur d'Objet, slectionnez LoginPrompt et basculez la valeur sur False (pour
viter avoir taper un mot de passe)
basculez Connected sur True
s'il n'y a aucune exception, cela signifie que la connexion a russi

Puis nous ajoutons les composants pour afficher une Table:


slectionnez un tAdoDataDataSet

et posez-le sur la Forme


slectionnez sa proprit Connection et reliez-la AdoConnection1
slectionnez sa proprit CommandText, et cliquez sur l'ellipse ...
le dialogue de requte Sql est prsent, avec, dans la ListBox des Tables les Tables que
nous avons places dans notre rpertoire:

cliquez une Table, "Add table", les colonnes (par exemple "*") et "Add Field". Dans
notre cas la requte est:
SELECT *
FROM customer
slectionnez Active et basculez sa valeur sur True
posez un tDataSource sur la Forme
voici le rsultat:

3 - Commentaires sur l'utilisation d'ADO et Paradox


3.1 - Motivations
La raison majeure qui pousse les dveloppeurs utiliser Paradox avec ADO est de
pouvoir conserver des donnes Paradox tout en remplaant le BDE par ADO.
Le BDE souffre en effet des dfauts suivants:

tout d'abord il est "deprecated". Ce mot inquitant signifie simplement que


depuis 2001 Codegear ne touche plus au produit. Mais il est tout de mme
utilisables par toutes les versions, et donc, malgr son remplacement
par dBExpress ou ADO en 2001, il figure toujours dans les versions Delphi,
y compris la dernire version de Rad Studio2007
au niveau fonctionnement, quelques problmes de gestion de caches peuvent
se manifester. Ceux ci surviennent surtout lorsqu'une application provoque
des erreurs en mode conception, et que le dveloppeur utilise
d'Explorateur de bases de donnes
et son plus gros dfaut est la lourdeur du dploiement. Chaque application
fournie aux client doit utiliser le BDE. Certes il est possible de crer des
scripts InstallShield, mais cela prend du temps

Pour Paradox, ADO est alors une bonne alternative. De plus ADO permet une
utilisation plus facile d'ASP.

3.2 - Perspectives ADO


Les personnes familires avec Ado.Net reconnatront naturellement la filiation
entre ADO et ADO.NET: connextion / commandes, composants d'accs pour les
requtes Sql. Nous avons d'ailleurs crit une srie d'articles
dtaillant l'architecture et l'utilisation d'ADO.NET ou encore Turbo Delphi et
Ado. Il existe nanmoins des diffrences trs importantes qui empcheraient un
portage trivial d'ADO vers ADO.Net

Pour les chanes de connexion, nous avons parl de Paradox, mais de nombreuses
autres possibilits pour le "Microsoft Jet 4.0" existent:

Microsoft Jet 1.0, Microsoft Jet 1.1, Microsoft Jet 2.0, Microsoft Jet 3.x,
Microsoft Jet 4.x
dBASE III, dBASE 4, dBASE 5
Excel 3.0, Excel 4.0, Excel 5.0, Excel 8.0, Excel 9.0
Exchange 4
Lotus WK1, Lotus WK3, Lotus WK4
Paradox 3.x, Paradox 4.x, Paradox 5.x, Paradox 7.x
Text 1.x
Html 1.x

3.3 - Formations et support Paradox / ADO


Ct Paradox, parmi les formations que nous assurons, les formations de bases de
donnes "desktop" avec le BDE utilisent depuis Delphi 1 soit dBase,
soit Paradox. Nous accueillons ce titre des stagiaires ayant en gnral maintenir
des applications dvelopps avec ces moteurs.
Nous organisons aussi, sur demande, des formations ADO, en utilisant en gnral
Access, Sql Server, voire Excel. Mais, comme indiqu ici, Paradox est
naturellement possible.
En fait, depuis quelques annes, nous assurons de plus en plus de formations
correspondant des besoins spcifiques que des formations gnrales figurant au
catalogue de l'Institut Pascal. Ceci correspond la fois des besoins de portage de
projets crits avec les premires versions de Delphi ainsi qu' l'volution des
comptences d'quipes de dveloppeurs vers de nouvelles technologies.

Ct dveloppement, nous assistons aussi certains clients dans le cadre de support


ou dveloppement sur mesure

soit pour migrer de Paradox vers d'autres moteurs Sql (Interbase / Firebird,
Sql Server, Oracle etc)
soit, en conservant les donnes Paradox, pour utiliser Ado comme
composants d'accs

Le but de ce type de dveloppement tant de conserver au maximum les donnes et


la logique mtier contenus dans les applications Paradox. Et le traitement va de la
simple conversion des fichiers de donnes qui peut tre en grand partie
automatise, une extraction des rgles mtier, voir restructuration de l'application.