I. Qu’est que OCI ?▲
OCI (Oracle Call Interface) est une librairie dynamique fournissant, à travers un ensemble de fonctions, la possibilité d’un contrôle quasi total à distance d’une base de données Oracle.
Les commandes se situent donc à un niveau très bas, ce qui entraîne leur complexité.
Parmi les possibilités de OCI, il existe :
- le support de SQL dynamique ;
- la manipulation (insertion…) des données par tableaux ;
- manipulation des objets (types définis par l’utilisateur, type collection…) et de métadonnées…
II. Structure générale d’une application OCI▲
La structure générale d’un programme ou application qui utilise les OCI se présente sous la forme suivante :
- Initialisation de l’environnement de programmation ;
- Allocation des différents handles, représentés sous la forme d’entiers longs dans 4D, et permettant de se connecter au serveur Oracle et de définir la session de l’utilisateur ;
- Préparation des requêtes SQL à exécuter ;
- Exécution des requêtes SQL ;
- Fermeture de la session utilisateur et déconnexion du serveur ;
- Libération des handles alloués.
Nous allons illustrer, un peu plus loin dans ce document, chacune des étapes de cette structure, en l’implémentant avec le plug-in 4D For OCI.
III. La notion de Handle▲
Un handle est un pointeur sur une zone mémoire allouée par la librairie OCI. Dans 4D, un handle est un entier long, qui doit impérativement être alloué.
La manipulation (lecture/écriture) des informations véhiculées par ces handles se fait par les commandes OCIAttrGet(), pour la lecture, et OCIAttrSet(), pour l'écriture.
Il existe plusieurs types de handles structures hiérarchiquement et dépendant les uns des autres. Nous allons énumérer les principaux d'entre eux, notamment ceux que nous allons utiliser dans nos exemples ; pour les autres handles, il faudra consulter la documentation OCI fournie par Oracle.
La hiérarchie des handles se présente donc ainsi, du plus élevé au plus bas.
A - Le handle-environnement (environment handle). Toute allocation d'un autre type de handle lui est assujettie. C'est donc le premier handle alloué. Il est alloué par la commande spéciale OCIEnvCreate(). Ce handle définit le contexte dans lequel les commandes OCI vont être appelées. Par exemple, la manipulation d'objets comme les Collections ou les tables imbriquées (« nested tables ») ne sont possibles que dans l'environnement OCI_OBJECT.
B - Un handle-contexte (service context handle) qui détermine le contexte opérationnel et auquel sont associés les handles suivants :
- un handle-serveur (server handle) qui identifie la source de données Oracle ;
- un handle-session (user session handle) qui définit l'accès de l'utilisateur ;
- un handle-transaction qui définit la transaction dans laquelle se déroulent la ou les requêtes SQL ;
-
un handle-requete (statement handle) qui définit la requête SQL. Ce handle doit être alloué. Les handles suivants lui sont associés :
- - un handle-define (define handle) qui recouvre les variables de sortie. En d'autres termes, ce handle sert pour les requêtes de type SELECT, à récupérer les données de la table Oracle dans 4D (sortie dans 4D). Ce handle est automatiquement alloué dans 4D For OCI,
- - un handle-bind (bind handle) qui recouvre les variables en entrée. En d'autres termes, ce handle sert pour les requêtes de type INSERT, UPDATE et DELETE, a ajouter, modifier ou supprimer des données dans Oracle, à partir de données provenant de 4D (entrée dans Oracle). Ce handle est automatiquement alloué dans 4D For OCI.
Il est important de comprendre que les handles serveur, session et transaction sont eux-mêmes traités comme des attributs du handle-contexte. Il en va de même pour les handles define et bind vis-a-vis du handle-requete.
Il existe aussi un handle-erreur. Ce handle sort de la hiérarchie, dans la mesure où tout appel a une commande OCI, nécessite la vérification de ce handle.
Le point fort des handles est qu'ils évitent le recours a une multitude de variables, chacune manipulant une information particulière. Pour ce faire, les handles pointent des zones de stockage d'informations. Ces informations sont retrouvées une à une en lisant un à un les attributs du handle.
IV. Mise en œuvre du plug-in 4D For OCI▲
Nous allons maintenant mettre en œuvre avec le plug-in 4D For OCI, les étapes présentées pour la structure générale d’un programme OCI.
Initialisation de l’environnement et allocations▲
Ici, on reprend les étapes 1 (Initialisation de l’environnement) et 2 (allocation des handles) de la structure du programme OCI.
Il s’agit d’une méthode 4D permettant à un utilisateur de se connecter à la base Oracle. Afin de ne pas alourdir le code, nous avons omis la gestion des éventuelles erreurs. Ce code peut paraître long pour une simple connexion. Il faut savoir que le plug-in 4D For OCI fournit une méthode de connexion simple OCILogon(), ce qui évite beaucoup d’allocations de handles… et raccourcit sensiblement le code. L’intérêt de ce code est donc avant tout didactique.
La méthode commence par allouer les différents handles, dans l’ordre de la hiérarchie, telle que décrit plus haut, du sommet vers la base. Tous les handles sont alloués par rapport au handle maître, le handle-environnement.
OCIServerAttach() crée un accès au serveur Oracle, en associant la chaîne de connexion au handle-serveur.
OCISessionBegin() démarre la session proprement dite, en établissant la connexion.
Code source de la méthode projet OCI_CONNECTER :
`Méthode : CONNECTER
`Exemple d'Appel de la méthode : CONNECTER ("SCOTT";"TIGER";"ORAQA")
`usage : permet de connecter un utilisateur à une base Oracle
`$1 : nom de l'utilisateur
`$2 : mot de passe
`$3 : chaîne de connexion ou nom du service oracle définis dans le fichier 'tnsnames.ora'
C_ALPHA(
255
;
${1
})
C_ENTIER LONG
(
envhp)
`handle de l'environnement
C_ENTIER LONG
(
svchp)
`handle-contexte
C_ENTIER LONG
(
authp)
`handle-session
C_ENTIER LONG
(
srvhp)
`handle-serveur
C_ENTIER LONG
(
$status
)
`retour des commandes OCI
` Allocations des handles
$status
:=
OCIEnvCreate (
envhp;
OCI_DEFAULT)
`handle d'environnement. Environnment 'Défaut'
`(Pas de traitement d'objets...)
$status
:=
OCIHandleAlloc (
envhp;
errhp;
OCI_HTYPE_ERROR )
`handle sur les erreurs éventuelles
$status
:=
OCIHandleAlloc (
envhp;
svchp;
OCI_HTYPE_SVCCTX )
`handle-contexte
$status
:=
OCIHandleAlloc (
envhp;
authp;
OCI_HTYPE_SESSION )
`handle-session
$status
:=
OCIHandleAlloc (
envhp;
srvhp;
OCI_HTYPE_SERVER )
`handle-serveur
`création d'un accès au serveur en affectant la chaîne de connexion au handle du serveur
$status
:=
OCIServerAttach (
srvhp;
errhp;
$3
)
`affectation du handle-serveur au handle-contexte
$status
:=
OCIAttrSetVal (
svchp;
srvhp;
OCI_ATTR_SERVER ;
errhp)
`mise à jour des attributs nom et mot de passe du handle-session
`avec les paramètres fournis à cette méthode
$status
:=
OCIAttrSetText (
authp;
$1
;
OCI_ATTR_USERNAME ;
errhp)
$status
:=
OCIAttrSetText (
authp;
$2
;
OCI_ATTR_PASSWORD ;
errhp)
`affectation du handle-session au handle-contexte
$status
:=
OCIAttrSetVal (
svchp;
authp;
OCI_ATTR_SESSION ;
errhp)
`démarrage de la session utilisateur
$status
:=
OCISessionBegin (
svchp;
errhp;
authp;
OCI_CRED_RDBMS ;
OCI_DEFAULT )
Préparation et exécution des requêtes SQL▲
Ici, on reprend les étapes 3 (préparation de la requête SQL) et 4 (exécution de la requête SQL) de la structure du programme OCI. On va fournir quatre méthodes, chacune implémentant une requête SQL fondamentale (SELECT, INSERT, UPDATE, DELETE).
SELECT▲
La méthode ci-après permet de récupérer les valeurs des colonnes de la table Oracle « emp », table des employés, en exécutant la requête SQL SELECT.
La méthode commence par allouer un handle pour la requête SQL (handle-requête).
Cette allocation est typique de chaque méthode qui travaille sur les requêtes (INSERT, UPDATE, DELETE).
La méthode sélectionne tous les employés, mais uniquement certaines colonnes (champs).
Ici on sélectionne le numéro d’employé (empno), le nom (ename), son métier (job) et la date d’embauche (hiredate).
Une fois définis et initialisés le libellé de la requête SQL et le handle-requête, ils sont mis en relation par la commande OCIStmtPrepare().
Pour chaque colonne impliquée dans la requête SQL, nous associons un tableau 4D pour récupérer l’ensemble des valeurs. Pour mettre en relation le tableau 4D et la colonne Oracle visée, on utilise la commande OCIDefineByPos().
Cette commande attend le numéro de la colonne mentionnée dans la requête SQL.
Le type Date étant particulier dans Oracle, puisqu’il est composé d’une partie date et d’une partie heure, nous avons utilisé ici la même commande OCIDefineByPos().
En fait, cette possibilité a été utilisée ici, car la composante heure ne nous intéresse pas.
Dans le cas contraire, nous aurions été obligés d’utiliser la commande OCIDefineDateByPos().
Code source de la méthode projet OCI_SELECT :
C_TEXTE
(
$requete_sql
)
`libellé de la requête SQL
C_ENTIER LONG
(
$status
)
`code retour des commandes OCI
C_ENTIER LONG
(
$errhp
)
`handle-erreur
C_ENTIER LONG
(
$stmthp
)
`handl- requête
C_ENTIER LONG
(
$define
)
`handle-define
`informations à récupérer de la table Oracle nommée 'emp'
C_ENTIER LONG
(
$numero_colonne
)
`numéro de la colonne à récupérer après exécution de la requête SQL
TABLEAU ENTIER LONG
(
tls_empno;
20
)
`numéros d'employés (colonne 'empno')
TABLEAU ALPHA(
255
;
tas_ename;
20
)
`noms d'employés (colonne 'ename' de la table 'emp')
TABLEAU ALPHA(
255
;
tas_job;
20
)
`métiers des employés (colonne 'job' de la table 'emp')
TABLEAU DATE
(
tds_hiredate;
20
)
`dates d'embauche (colonne 'hiredate' de la table 'emp')
C_ENTIER LONG
(
$max_emp
)
`nombre maximum d'employés à récupérer
C_ENTIER LONG
(
null_ind1;
null_ind2;
null_ind3)
`variables indicateurs
`requête-sélection de la liste des numéros, noms, métiers et dates d'embauche de tous les employés de
`la table oracle ‘emp’
$requete_sql
:=
"SELECT empno, ename, job,hiredate FROM emp"
`allocation du handle-requête. Le handle-environnement envhp
`a été alloué dans la méthode de connexion OCI_CONNECTER
$status
:=
OCIHandleAlloc (
envhp;
$stmthp
;
OCI_HTYPE_STMT )
`allocation du handle-erreur
$status
:=
OCIHandleAlloc (
envhp;
$errhp
;
OCI_HTYPE_ERROR )
`affectation du libellé de la requête SQL au handle-requête
$status
:=
OCIStmtPrepare (
$stmthp
;
$errhp
;
$requete_sql
;
OCI_DEFAULT )
`variables-indicateurs pour la commande OCIDefineByPos().
`Les informations retournées par ces variables sont inutiles dans notre exemple
`ces variables servent notamment à savoir s'il y a des valeurs NULL, tronquées.
null_ind1:=
0
null_ind2:=
0
null_ind3:=
0
`réalisation du Define pour chaque colonne définie dans la requête SQL
`On notera que $define, qui définit le handle-define, n'a pas nécessité d'être explicitement alloué.
$numero_colonne
:=
1
`numéro de colonne pointée, remontée par l'exécution de la requête (empno)
$status
:=
OCIDefineByPos (
$stmthp
;
$define
;
$errhp
;
$numero_colonne
;->
tls_empno;
SQLT_INT ;
->
null_ind1;->
null_ind2;->
null_ind3;
OCI_DEFAULT )
$numero_colonne
:=
2
`numéro de colonne pointée, remontée par l'exécution de la requête (ename)
$status
:=
OCIDefineByPos (
$stmthp
;
$define
;
$errhp
;
$numero_colonne
;->
tas_ename;
SQLT_STR ;
->
null_ind1;->
null_ind2;->
null_ind3;
OCI_DEFAULT )
$numero_colonne
:=
3
`numéro de colonne pointée, remontée par l'exécution de la requête (job)
$status
:=
OCIDefineByPos (
$stmthp
;
$define
;
$errhp
;
$numero_colonne
;->
tas_job;
SQLT_STR ;
->
null_ind1;->
null_ind2;->
null_ind3;
OCI_DEFAULT )
$numero_colonne
:=
4
`numéro de colonne pointée, remontée par l'exécution de la requête (hiredate)
$status
:=
OCIDefineByPos (
$stmthp
;
$define
;
$errhp
;
$numero_colonne
;->
tds_hiredate;
SQLT_ODT ;
->
null_ind1;->
null_ind2;->
null_ind3;
OCI_DEFAULT )
`récupération des employés à concurrence de 20 enregistrements
$max_emp
:=
20
`exécution de la requête SQL
$status
:=
OCIStmtExecute (
svchp;
$stmthp
;
$errhp
;
$max_emp
;
0
;
0
;
0
;
OCI_DEFAULT )
`libération du handle-requête
$status
:=
OCIHandleFree (
$stmthp
)
`libération du handle-erreur
$status
:=
OCIHandleFree (
$errhp
)
INSERT▲
La méthode ci-après permet d’ajouter des enregistrements dans la table Oracle « emp », en exécutant la requête SQL INSERT.
L’ensemble des valeurs que l’on va ajouter est stocké dans des tableaux 4D. On associe les données à ajouter avec les colonnes Oracle, en utilisant dans la requête SQL le caractère « : ». Ce marquage peut être utilisé ensuite de deux manières : l’association données 4D/colonnes Oracle est faite soit par position (commande OCIBindByPos()) ou par nom (commande OCIBindByName()).
À noter que pour l’insertion de valeurs de type date, on utilise la commande OCIBindDateByPos(). Cette commande attend le passage de variables de types date et heure, afin de représenter intégralement le type Oracle ODT (Object Date Time) qui contient les deux parties date et heure. La partie heure ne nous intéressant pas, nous avons utilisé une variable heure 4D avec une valeur nulle.
Code source de la méthode projet OCI_INSERT :
C_TEXTE
(
$requete_sql
)
`libellé de la requête SQL
C_ENTIER LONG
(
$status
)
`code retour des fonctions OCI
C_ENTIER LONG
(
$errhp
)
`handle-erreur
C_ENTIER LONG
(
$stmthp
)
`handle-requête
C_ENTIER LONG
(
$bind
)
`handle-bind
C_POINTEUR
(
pnull_ind1;
pnull_ind2;
pnull_ind3)
`variables indicateurs (voir la méthode `OCI_SELECT)
C_ENTIER LONG
(
$nb_emp
)
`nombre d'employés à insérer
`déclaration des données à insérer
TABLEAU ENTIER LONG
(
tli_empno;
3
)
`colonne empno
TABLEAU ALPHA(
255
;
tai_ename;
3
)
`colonne ename
TABLEAU ALPHA(
255
;
tai_job;
3
)
`colonne job
TABLEAU ENTIER LONG
(
tli_mgr;
3
)
`colonne mgr
TABLEAU DATE
(
tdi_hiredate;
3
)
`colonne hiredate
C_HEURE
(
null_time)
`variable de type heure, en complément de la colonne date, pour représenter
`intégralement le type ODT oracle, qui comprend la date et l’heure
TABLEAU ENTIER LONG
(
tli_sal;
3
)
`colonne sal
TABLEAU ENTIER LONG
(
tli_comm;
3
)
`colonne comm
TABLEAU ENTIER LONG
(
tli_deptno;
3
)
`colonne deptno
`remplissage des données à insérer
`colonne empno
tli_empno{1
}:=
1111
tli_empno{2
}:=
2222
tli_empno{3
}:=
3333
`colonne ename
tai_ename{1
}:=
"Joel"
tai_ename{2
}:=
"Catherine"
tai_ename{3
}:=
"Marianne"
`colonne job
tai_job{1
}:=
"analyste"
tai_job{2
}:=
"vendeur"
tai_job{3
}:=
"manager"
`colonne mgr
tli_mgr{1
}:=
7902
tli_mgr{2
}:=
7698
tli_mgr{3
}:=
7788
`colonne hiredate
tdi_hiredate{1
}:=
!01/01/03!
tdi_hiredate{2
}:=
!02/01/03!
tdi_hiredate{3
}:=
!03/01/03!
null_time:=
?00:00:00?
`colonne sal
tli_sal{1
}:=
1915
tli_sal{2
}:=
2012
tli_sal{3
}:=
1713
`colonne comm
tli_comm{1
}:=
100
tli_comm{2
}:=
200
tli_comm{3
}:=
150
`colonne deptno
tli_deptno{1
}:=
20
tli_deptno{2
}:=
30
tli_deptno{3
}:=
20
`requête SQL d'insertion des données. On précise les colonnes de la table 'emp', qui seront remplies
`noter l'utilisation du caractère ":" pour indiquer qu'une variable fournira les données à insérer
`cette indication sera exploitée soit par nom (nom de la variable 4D) soit par position (numéro de
`position dans la requête).
$requete_sql
:=
"INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)"
$requete_sql
:=
$requete_sql
+
" VALUES (:tli_empno, :tai_ename, :tai_job, :tli_mgr,"
$requete_sql
:=
$requete_sql
+
" :tdi_hiredate, :tli_sal,:tli_comm,:tli_deptno)"
`allocation du handle-requête
$status
:=
OCIHandleAlloc (
envhp;
$stmthp
;
OCI_HTYPE_STMT )
`allocation du handle erreur
$status
:=
OCIHandleAlloc (
envhp;
$errhp
;
OCI_HTYPE_ERROR )
`affectation du libellé de la requête SQL au handle requête
$status
:=
OCIStmtPrepare (
$stmthp
;
$errhp
;
$requete_sql
;
OCI_DEFAULT )
`réalisation du bind pour chaque colonne de la requête. Le bind est effectué par position
`remarquez que le handle-bind $bind n'a pu à être explicitement alloué
$status
:=
OCIBindByPos (
$stmthp
;
$bind
;
$errhp
;
1
;->
tli_empno;
SQLT_INT ;
pnull_ind1;
pnull_ind2;
pnull_ind3;
OCI_DEFAULT ;
BIND_IN )
$status
:=
OCIBindByPos (
$stmthp
;
$bind
;
$errhp
;
2
;->
tai_ename;
SQLT_STR ;
pnull_ind1;
pnull_ind2;
pnull_ind3;
OCI_DEFAULT ;
BIND_IN )
$status
:=
OCIBindByPos (
$stmthp
;
$bind
;
$errhp
;
3
;->
tai_job;
SQLT_STR ;
pnull_ind1;
pnull_ind2;
pnull_ind3;
OCI_DEFAULT ;
BIND_IN )
$status
:=
OCIBindByPos (
$stmthp
;
$bind
;
$errhp
;
4
;->
tli_mgr;
SQLT_INT ;
pnull_ind1;
pnull_ind2;
pnull_ind3;
OCI_DEFAULT ;
BIND_IN )
`le type Oracle ODT recouvre à la fois la date et l'heure
`comme la valeur de l'heure ne nous intéresse pas ici, celle-ci est donc passée comme nulle
$status
:=
OCIBindDateByPos (
$stmthp
;
$bind
;
$errhp
;
5
;->
null_time;->
tdi_hiredate;
SQLT_ODT ;
pnull_ind1;
pnull_ind2;
pnull_ind3;
OCI_DEFAULT ;
BIND_IN )
$status
:=
OCIBindByPos (
$stmthp
;
$bind
;
$errhp
;
6
;->
tli_sal;
SQLT_INT ;
pnull_ind1;
pnull_ind2;
pnull_ind3;
OCI_DEFAULT ;
BIND_IN )
$status
:=
OCIBindByPos (
$stmthp
;
$bind
;
$errhp
;
7
;->
tli_comm;
SQLT_INT ;
pnull_ind1;
pnull_ind2;
pnull_ind3;
OCI_DEFAULT ;
BIND_IN )
$status
:=
OCIBindByPos (
$stmthp
;
$bind
;
$errhp
;
8
;->
tli_deptno;
SQLT_INT ;
pnull_ind1;
pnull_ind2;
pnull_ind3;
OCI_DEFAULT ;
BIND_IN )
`nombre d'employés à insérer
$nb_emp
:=
3
`exécution de la requête SQL
$status
:=
OCIStmtExecute (
svchp;
$stmthp
;
$errhp
;
$nb_emp
;
0
;
0
;
0
;
OCI_DEFAULT )
`confirmation de l'insertion par validation immédiate de la transaction
`sinon, l'insertion est effective à la fermeture de la session
status:=
OCITransCommit (
svchp;
$errhp
;
0
)
`libération du handle-requête
$status
:=
OCIHandleFree (
$stmthp
)
`libération du handle-erreur
$status
:=
OCIHandleFree (
$errhp
)
UPDATE▲
La méthode ci-après permet de mettre à jour les enregistrements précédemment ajoutés dans la table Oracle « emp », en exécutant la requête SQL UPDATE.
Dans cette méthode, nous mettons à jour les 3 enregistrements ajoutés par la requête INSERT. Nous modifions uniquement les noms (colonne ename).
À remarquer que nous avons fait ici un Bind (association variable 4D/colonne Oracle) par nom. Dans la précédente requête INSERT, nous l’avons fait par position.
Code source de la méthode projet OCI_UPDATE :
C_TEXTE
(
$requete_sql
)
`libellé de la requête SQL
C_ENTIER LONG
(
$status
)
`code retour des commandes OCI
C_ENTIER LONG
(
$errhp
)
`handle-erreur
C_ENTIER LONG
(
$stmthp
)
`handle-requête
C_ENTIER LONG
(
$bind
)
`handle-bind
C_POINTEUR
(
pnull_ind1;
pnull_ind2;
pnull_ind3)
`variables indicateurs (voir la méthode `OCI_SELECT)
C_ENTIER LONG
(
$nb_emp
)
`nombre d'employés à mettre à jour
`déclaration des données à modifier
TABLEAU ENTIER LONG
(
tlu_empno;
3
)
`colonne empno
TABLEAU ALPHA(
255
;
tau_ename;
3
)
`colonne ename
`critère de recherche dans la requête SQL pour la mise à jour
tlu_empno{1
}:=
1111
tlu_empno{2
}:=
2222
tlu_empno{3
}:=
3333
`données modifiées
tau_ename{1
}:=
"JJ"
tau_ename{2
}:=
"CC"
tau_ename{3
}:=
"MM"
`mise à jour des noms des employés de la table oracle emp dont les numéros sont 1111,2222, et 3333
`rappel : ce sont les données qui ont été insérées qui sont modifiées
$requete_sql
:=
"UPDATE emp SET ename=:les_noms WHERE empno=:les_numeros"
`allocation du handle-requête
$status
:=
OCIHandleAlloc (
envhp;
$stmthp
;
OCI_HTYPE_STMT )
`allocation du handle erreur
$status
:=
OCIHandleAlloc (
envhp;
$errhp
;
OCI_HTYPE_ERROR )
`affectation du libellé de la requête SQL au handle-requête
$status
:=
OCIStmtPrepare (
$stmthp
;
$errhp
;
$requete_sql
;
OCI_DEFAULT )
`on effectue un Bind par nom. Sur le INSERT, on l'a effectué par position
$status
:=
OCIBindByName (
$stmthp
;
$bind
;
$errhp
;
":les_noms"
;->
tau_ename;
SQLT_STR ;
pnull_ind1;
pnull_ind2;
pnull_ind3;
OCI_DEFAULT ;
BIND_IN )
$status
:=
OCIBindByName (
$stmthp
;
$bind
;
$errhp
;
":les_numeros"
;->
tlu_empno;
SQLT_INT ;
pnull_ind1;
pnull_ind2;
pnull_ind3;
OCI_DEFAULT ;
BIND_IN )
`exécution de la requête SQL
$nb_emp
:=
3
$status
:=
OCIStmtExecute (
svchp;
$stmthp
;
$errhp
;
$nb_emp
;
0
;
0
;
0
;
OCI_DEFAULT )
`confirmation de la modification par validation immédiate de la transaction
`sinon, la mise à jour est effective à la fermeture de la session
status:=
OCITransCommit (
svchp;
$errhp
;
0
)
`libération du handle-requête
$status
:=
OCIHandleFree (
$stmthp
)
`libération du handle-erreur
$status
:=
OCIHandleFree (
$errhp
)
DELETE▲
La méthode ci-après permet de supprimer les enregistrements précédemment ajoutés dans la table Oracle « emp », en exécutant la requête SQL DELETE.
Ici, nous supprimons les enregistrements que nous avons précédemment insérés et mis à jour. Le code de cette méthode est à rapprocher de la requête UPDATE, dans la mesure où nous effectuons aussi un Bind (association variable 4D/colonne Oracle) par nom.
Code source de la méthode projet OCI_DELETE :
C_TEXTE
(
$requete_sql
)
`libellé de la requête SQL
C_ENTIER LONG
(
$status
)
`code retour des commandes OCI
C_ENTIER LONG
(
$errhp
)
`handle-erreur
C_ENTIER LONG
(
$stmthp
)
`handle-requête
C_ENTIER LONG
(
$bind
)
`handle-bind
C_POINTEUR
(
pnull_ind1;
pnull_ind2;
pnull_ind3)
`variables indicateurs (voir la méthode `OCI_SELECT)
C_ENTIER LONG
(
$nb_emp
)
`nombre d'employés à supprimer dans la base Oracle
TABLEAU ENTIER LONG
(
tld_empno;
3
)
`déclaration des données à rechercher pour la suppression
`les numéros des enregistrements des employés à supprimer de la base Oracle
tld_empno{1
}:=
1111
tld_empno{2
}:=
2222
tld_empno{3
}:=
3333
`suppression des employés de la table oracle emp dont les numéros sont 1111,2222, et 3333
`ces employés ont été ajoutés par la méthode OCI_INSERT
$requete_sql
:=
"DELETE FROM emp WHERE empno=:numemp"
`allocation du handle requête
$status
:=
OCIHandleAlloc (
envhp;
$stmthp
;
OCI_HTYPE_STMT )
`allocation du handle erreur
$status
:=
OCIHandleAlloc (
envhp;
$errhp
;
OCI_HTYPE_ERROR )
`affectation du libellé de la requête SQL au handle-requête
$status
:=
OCIStmtPrepare (
$stmthp
;
$errhp
;
$requete_sql
;
OCI_DEFAULT )
`on effectue un Bind par nom.
`remarquez que le handle-bind $bind n'a pas nécessité d'être explicitement alloué
$status
:=
OCIBindByName (
$stmthp
;
$bind
;
$errhp
;
":numemp"
;->
tld_empno;
SQLT_INT ;
pnull_ind1;
pnull_ind2;
pnull_ind3;
OCI_DEFAULT ;
BIND_IN )
`exécution de la requête SQL
$nb_emp
:=
3
$status
:=
OCIStmtExecute (
svchp;
$stmthp
;
$errhp
;
$nb_emp
;
0
;
0
;
0
;
OCI_DEFAULT )
`confirmation de la suppression par validation immédiate de la transaction
`sinon, la suppression est effective à la fermeture de la session
$status
:=
OCITransCommit (
svchp;
$errhp
;
0
)
`libération du handle-requête
$status
:=
OCIHandleFree (
$stmthp
)
`libération du handle-erreur
$status
:=
OCIHandleFree (
$errhp
)
Fermeture de la connexion et libération des handles▲
Ici, on reprend les étapes 5 (fermeture de la session) et 6 (libération des handles) de la structure du programme OCI.
C’est la fonction OCIHandleFree() qui est utilisée. Les handles étant structurés hiérarchiquement, la libération d’un handle-père entraîne celle des handles-fils.
La méthode ci-après montre le code pour se déconnecter du serveur Oracle. On aurait pu plus simplement utiliser la méthode OCILogoff() en lieu et place de ce code.
OCISessionEnd() supprime la session.
OCIServerDetach() supprime le chemin d’accès au serveur oracle.
On peut, après cela, libérer un à un les handles, ou alors libérer le handle environnement, ce qui entraînera la libération des autres handles.
Code source de la méthode projet OCI_DECONNECTER :
`Méthode : DECONNECTER
`Appel de la méthode : DECONNECTER
`usage : permet de déconnecter l'utilisateur couramment connecté
C_ENTIER LONG
(
$status
)
`retour des commandes OCI
$status
:=
OCISessionEnd (
svchp;
errhp;
authp)
`suppression de la session utilisateur
$status
:=
OCIServerDetach (
srvhp;
errhp)
`suppression de l'accès à la source de données
$status
:=
OCIHandleFree (
envhp)
`libérer le handle-environnement qui libérera automatiquement à
`son tour les autres handles
`On aurait pu aussi utiliser la commande OCICleanUp() à la place de OCIHandleFree,
`pour libérer d'un coup tous les handles alloués
`status:=OCICleanUp ()
V. Utilisation de la base de test OCI▲
Afin d'utiliser la base de test, il faut impérativement que les conditions suivantes soient réunies :
- sur Windows, il faut avoir installé la partie cliente d'Oracle, fournie sur le CD-Rom ou le site web d'Oracle ;
-
sur Mac OS X, il faut suivre les instructions fournies dans le « Readme » fourni avec le produit, notamment :
- Copier dans /private/etc/ votre fichier 'tnsnames.ora' , mis à jour avec vos propres paramètres (nom de service, adresse IP de la base Oracle) pour localiser votre base Oracle, et attribuer le droit de lecture sur le fichier.
Par exemple, en utilisant le Terminal de OS X, on saisira :
- Copier dans /private/etc/ votre fichier 'tnsnames.ora' , mis à jour avec vos propres paramètres (nom de service, adresse IP de la base Oracle) pour localiser votre base Oracle, et attribuer le droit de lecture sur le fichier.
sudo cp /chemin d'accès à votre fichier tnsnames.ora /private/etc/tnsnames.ora
sudo chmod 777 /private/etc/tnsnames.ora
La première ligne de commande saisie dans le terminal d'OS X sert à copier le fichier en prenant temporairement la 'casquette' (profil) du root. Le mot de passe root vous sera demandé.
La deuxième ligne de commande sert à attribuer tous les droits pour tout le monde.
Si vous avez des problèmes pour la saisie des chemins d'accès au fichier à copier, une astuce consiste à faire glisser le fichier sur la fenêtre du Terminal.
Le chemin d'accès du fichier y apparaît alors.
- Dans le dossier 'Oracle items' fourni en téléchargement avec le plug-in 4D For OCI, on trouvera la dll 'libcIntsh.dylib'.
- Il faudra copier cette dll dans /usr/lib et donner les droits de lecture et exécution sur cette librairie :
sudo cp /chemin d'accès à votre fichier libcIntsh.dylib /usr/lib/
sudo chmod 555 /usr/lib/libcIntsh.dylib
- La première ligne sert à copier la DLL, la seconde à attribuer les droits de lecture et exécution pour tout le monde.
La base de test des OCI fournie avec cette note technique, utilisable avec une version de 4D 2003 ou supérieure, reprend les codes sources expliqués dans la présente note. Il faudra bien veiller à changer vos paramètres de connexion fournis à la méthode projet OCI_CONNECTER().
Cette base ayant un but uniquement didactique, il faudra bien suivre les pages du dialogue de démonstration dans l’ordre, notamment l’ordre insertion des données, mise à jour des données et suppression des données.
En mode Menus Créés, il faut choisir le menu \Fichier\Demo… et vous laisser guider.
VI. Conclusion▲
Cette note vous a donné un aperçu de l’utilisation du plug-in 4D For OCI, en présentant des exemples d’utilisation des quatre types fondamentaux de requêtes SQL (SELECT, INSERT, UPDATE, DELETE) ainsi qu’un exemple de connexion/déconnexion à une base Oracle. Nous espérons que cette note vous a donné envie d’explorer plus avant l’utilisation du plug-in 4D For OCI, en exploitant par exemple, le fonctionnement de l’environnement Objet, qui est sans aucun doute original et puissant.
VII. Base exemple▲
Téléchargez la base exemple :
Base pour Windows
Base pour Mac Os