C déclaration des variables d'erreur et de contrôle propres à Ingres C exec sql include sqlca C C déclaration des variables locales obligatoire dans un bloc begin-end C exec sql begin declare section integer cdepcom, cpsdc, i integer*2 cavg, cdept, ctranche exec sql end declare section C C fin des déclarations C traitement des conditions d'erreur C exec sql whenever sqlerror stop C C connexion à la base C exec sql connect parcauto C C lecture des variables locales on prévoit d'aller en 2 en fin de fichier i = 0 1 read(5, *, end=2) cdepcom, cdept, ctranche, cagv C chargement répété de lignes ; les variables du langage présentes C dans un ordre SQL sont obligatoirement précédées par ":" exec sql repeated insert into commune & (depcom,dept,tranche,agv) & values & (:cdepcom,:cdept,:ctranche,:cagv) i = i + sqlerr(3) go to 1 C C traitement de fin de saisie C 2 print *,' on a rentré ', i, ' lignes' C C fermeture et déconnexion de la base C exec sql disconnect stop end
$ dclgen fortran parc commune commune.incsql commune cas particulier en fortran : si vous voulez le résultat dans des variables fortran, plutôt que dans une structure, il faut lancer dclgen ainsi : Dans le programme, on se sert de ce fichier à l'aide de l'ordre exec sql include, placé dans le bloc de déclaration. ex : Cette structure permettra d'échanger des données entre le programme et la base de données. L'ordre exec sql include permet l'insertion de tout fichier de source embedded sql en tout point d'un programme.
exec sql begin /* toute séquence de programmation du langage hôte */ /* sans appel à la base de données */ exec sql end
exec sql declare n_curs cursor for select ... X-3-3-3 transfert de la ligne résultat avec l'ordre fetch exec sql fetch n_curs into :var1 {, :vari} | :struct exec sql close n_curs values (:nom, :prenom, :age:manq1, :adresse:manq2)
C programme de sortie de tableau croisé C exec sql include sqlca C C l'ordre include ci-dessous suppose que l'on a utilisé dclgen C avec l'option -f77 avec pour préfixe la lettre c exec sql begin declare section exec sql include 'commune.incsql' integer i, j, icomt, itab(100,9) exec sql end declare section C C Lorsque les lignes résultats seront épuisées, on passe à 2 exec sql whenever not found go to 2 C exec sql connect parc C C1 -- déclaration de select exec sql select dept, tranche, count(depcom) & into :cdept, :ctranche, :cnb & from commune & group by dept, tranche exec sql begin C boucle de traitement (tout code sauf ordre sql) itab(cdept, ctranche) = cnb exec sql end C 2 -- fin d'usage du select exec sql disconnect C on passe à l'édition du tableau print (6,10) ((itab(i,j), & i=91,94) j= 3,8) 10 format (4i6) stop end La version ci-dessus est de loin la plus efficace ; ci-dessous, vous trouverez ce qu'aurait été, entre les commentaires C1 et C2, la programmation avec déclaration de curseur : exec sql declare cmpt cursor for & select dept, tranche, count(depcom) & from commune & group by dept, tranche C C ouverture du curseur exec sql open cmpt C début de boucle infinie 1 continue exec sql fetch cmpt into :cdept, :ctranche, & :icomt itab(cdept, ctranche) = icomt go to 1 2 continue exec sql close cmpt C2 -- le curseur est fermé le fichier programme doit être suffixé par .sf (pour fortran, .sc pour C) Il faut alors compiler et lier l'objet à la bibliothèque de sous-programmes fournie par Ingres, avec l'utilitaire du CIR "femb" : (utiliser cemb pour un programme C) femb et cemb sont des produits CIR, non renseignés dans les documentations Ingres.
C ceci est la copie d'un véritable programme utilisateur C C On a des demandes de voyages dans demande, C entre coo (origine) et cod (destination), C à une date déterminée. C On regarde l'existence de trains entre coo et cod C dans od2, puis l'on regarde, si ces trains C existent soit la veille, le jour, ou le lendemain C de la date déterminée C dans fcalt pour les trains trouves dans od2 C exec sql include sqlca exec sql begin declare section integer*2 hd, ic, dep, jd, md, arr, ha, ja, ma, ar integer*2 oho, ohd, dist, g1, g2, sens integer num, coo, cod, tabcol(2) integer como, comd, go, gd, tab1, tab2, numf,circ(2) exec sql end declare section
C Quand un curseur a balayé toutes les lignes d'un select, C on va à l'étiquette 100 exec sql whenever not found goto 100 C En cas d'erreur sql, on appelle le sous-programme C utilisateur erring exec sql whenever sqlerror call erring
open(10, file = 'erreurs1') C ouverture de la base exec sql connect baseod
C numf numéro de voyageur, on arrêtera la recherche à C ce numéro numf = 11904 C déclaration du curseur pour recherche des trains exec sql declare demande cursor for select 1 d.num, d.coo, d.cod, d.ar, d.dep, d.hd, d.jd, 2 d.md, d.arr, d.ha, d.ja, d.ma, 3 o.como, o.comd, o.go, o.gd, o.ho, o.hd, o.d, 4 o.tab1, 5 o.g1, o.tab2, o.g2, o.sens 6 from demande d, od2 o where 7 o.como = d.coo and 8 o.comd = d.cod and 9 num between 11900 and :numf 0 order by num; C déclaration du curseur pour recherche dans le calendrier C des trains
exec sql declare fcal cursor for 1 select tabcol, int4( right(trim(left(cal, 2 :dep +1)),3)) 3 from fcalt 4 where tabcol = :tab1 or tabcol = :tab2 C ouverture du curseur de recherche d'existence de train exec sql open demande 50 continue C boucle sur le curseur demande, récupération dans les C variables (autant et de même type que celles demandées C dans le select) exec sql fetch demande into :num, :coo, :cod, 1 :ar, :dep, 2 :hd, :jd, :md, :arr, :ha, :ja, :ma, 3 :como, :comd, :go, :gd, :oho, :ohd, :dist, 4 :tab1, :g1, :tab2, :g2, :sens print *,'num=',num if(tab1.eq.50002114.or.tab2.eq.50002114)goto 50 C pour chaque voyageur, on ouvre/ferme le curseur fcal exec sql open fcal do 10 i=1,2 C boucle sur le curseur fcal et récupération C dans des variables exec sql fetch fcal into :tabcol(i),:circ(i) 10 continue write (10,2000) tabcol(1),circ(1),tabcol(2),circ(2) 2000 format(2(i10,i4)) ic = circ(1) + circ(2) do 30 i = 1, 3 idix = 10**(i-1) if( mod( ic/idix, 10).eq.2) go to 31 30 continue go to 32 31 continue C C les trains intéressants sont stockes dans result, C avec la demande associée C exec sql insert into result values( 1 :num, :como, :comd, :go, :gd, :oho, :ohd, :dist, 2 :tab1, :g1, :tab2, :g2 ,:sens, :dep, :hd, :jd, 3 :md, :arr, :ha, :ja, :ma, :ic) print *, 'insert dans result' 32 continue C important, fermeture de fcal avant de passer au C voyageur suivant exec sql close fcal C goto 50 100 continue C si l'un des deux curseurs ne rend plus de recherche, C on arrive ici print *, 'juste apres 100' exec sql close fcal print *, 'num = ', num, 'tab1 = ', tab1, 'tab2 = ', tab2 write (10,3000) num,tab1,tab2 3000 format (3i10) exec sql close demande C Placer des commit aussi souvent que possible C quand il y a des modifs C dans une base, mais attention, il ferme tous les C curseurs ouverts exec sql commit exec sql disconnect stop end
subroutine erring C gère l'impression des messages d'erreur que peut C fournir un ordre sql exec sql include sqlca exec sql begin declare section character * 256 mess exec sql end declare section exec sql whenever sqlerror continue exec sql inquire_sql (:mess = ERRORTEXT) print *, 'sqlcod = ',sqlcod print *, mess return end La procédure décrite ci-dessous a 4 arguments ; cette procédure est à créer en interactif (généralement) dans isql. L'expression sur les dates calcule si la valeur de la colonne date (dans la table approchants) est comprise entre l'argument date plus ou moins 30 minutes ; le nombre de lignes trouvées est retourné par la variable iirowcount implicite dans les procédures. Cette valeur sera récupérée (voir source plus loin) dans la variable nbaset. Remarque : l'argument date n'est pas de type date, on utilise donc la fonction de conversion date(). Évidemment l'argument date doit avoir un bon format de présentation pour cette fonction. org char(1), dep char(3), com char(3), date char(17)) as begin insert into approchants select * from caracteristiques where org = :org and dep = :dep and com = :com and abs(interval('min', date - date(:date))) <= 30; return :iirowcount; end; retrix integer, num integer) as begin insert into lieux ( retrix, rout, cft, cadm, loc, nrte, ind, typr, pr, circ, nvoi, pcyc, prof, tplan, surf, lcs, ltpc, lbau, ltro, lacc, cr1, cr2, marq, etat, atac, atpr, ouv) select :retrix, rout, cft, cadm, loc, nrte, ind, typr, pr, circ, nvoi, pcyc, prof, tplan, surf, lcs, ltpc, lbau, ltro, lacc, cr1, cr2, marq, etat, atac, atpr, ouv from lieuxacc where num = :num; end; C extrait du programme fortran où ces procédures C sont utilisées C C on récupère dans nbaset le nb de lignes insérées C exec sql execute procedure insapproch (org = :org, + dep = :dep, com = :com, date = :date)
into
:nbaset
call prerr(iprint, 'insert into approchant') C C .... et plus loin dans le programme ..... C C insertion des lieux exec sql execute procedure inslieux (retrix =:retrix, + num=:num) call prerr(iprint, 'inslieux ') go to 2001 end /* programme d'exemple en C pour lire les temps de parcours dans la table "arcs" et mesurer les temps cpu et residents pris par cette lecture */ /*le programme est appelé en donnant le nom de la base en paramètre */ /* exemple : messai mabase */ #include <stdio.h> /* pour récupérer les codes d'erreurs */ exec sql include sqlca; main(arg_count,arg_vector) unsigned arg_count; char *arg_vector[]; { /* déclarations des variables locales du programme */ exec sql begin declare section; exec sql include "arcs.h"; /* le fichier arcs.h, créé par la commande dclgen, contient la description de la table "arcs" sous forme d'une structure C de ce type :*/ struct arcs_ { long numarc; long norig; long ndest; long longueur; long temps; } arcs; */ int time, timea, etime, etimea; int nba; int tparc[5000]; char basename[30]; exec sql end declare section; /*fin des déclarations */ nba=0; /* lecture du nom de base dans "basename" */ sscanf(arg_vector[1],"%s",basename); exec sql connect :basename; /*connexion a la base */ /* insertion du code pour interroger les variables _cpu_ms et _et_sec qui vont donner les temps cpu et elapsed */ exec sql select int4(dbmsinfo('_cpu_ms')) into :time; timea=time; exec sql select int4(dbmsinfo('_et_sec')) into :etime; etimea=etime; /* question sql: on va chercher, par une boucle, tous les tuples de la table "arcs", ranger les valeurs dans la structure arcs et prendre le temps de parcours dans un tableau tparc */ exec sql select * into :arcs from arcs; exec sql begin; tparc[nba]=arcs.temps; nba++; exec sql end; if(sqlca.sqlcode!=0) { printf("erreur %d\n",sqlca.sqlcode); } /* code pour rechercher les nouvelles valeurs de temps après la requête */ exec sql select int4(dbmsinfo('_cpu_ms')) into :time; exec sql select int4(dbmsinfo('_et_sec')) into :etime; /* on imprime les valeurs de temps mesurées */ printf("temps cpu %d ms\n",time-timea); printf("temps elapsed %d s\n",etime-etimea); /* déconnexion/ exec sql disconnect ; printf("nb arcs %d\n",nba); }
ESQL messai.sc: $ temps cpu 50 ms temps elapsed 0 s nb arcs 1950 $ |