\documentclass[a4paper,11pt]{article} \usepackage[utf8x]{inputenc} \usepackage[T1]{fontenc} \usepackage[french]{babel} \usepackage[a4paper,hmargin=20mm,vmargin=30mm]{geometry}%\usepackage{fullpage} \usepackage{lastpage} \usepackage{tikz,pgflibraryarrows,pgffor,pgflibrarysnakes} \usetikzlibrary{decorations.pathreplacing} \usepackage{url} \usepackage{comment} \usepackage{amsmath} \usepackage{amsthm} \theoremstyle{definition} \newtheorem{exemple}{Exemple}[section] \newtheorem{exercice}{Exercice}[section] \newtheorem{remarque}{Remarque}[section] \newtheorem{definition}{Définition}[section] \usepackage{makeidx} \usepackage[columnsep=9pt]{idxlayout} \usepackage{fancyvrb} %\makeindex \selectlanguage{french} \usepackage[font=small,labelfont=bf,justification=centering]{caption} \captionsetup[table]{name=Tableau} \usepackage{latexsym} \usepackage{amsfonts} \usepackage[normalem]{ulem} \usepackage{array} \usepackage{amssymb} \usepackage{graphicx} \usepackage{subfig} \usepackage{wrapfig} \usepackage{wasysym} \usepackage{enumitem} \usepackage{adjustbox} \usepackage{longtable} \usepackage{changepage} \usepackage{setspace} \usepackage{hhline} \usepackage{multicol} \usepackage{float} \usepackage{multirow} \usepackage{slashbox} \usepackage{color, colortbl} \definecolor{Gray}{gray}{0.9} \usepackage{fancyvrb} \usepackage{fancyhdr}% fancy header \usepackage{varwidth} \usepackage{alltt} \fancypagestyle{monstyle}{ %\fancyhead{} \renewcommand{\headrulewidth}{1pt} %% %\renewcommand{\footrulewidth}{0.4pt} \newcommand{\proc}{Pro$^{*}$C} % \fancyhead[LE]{\slshape \thepage/ \pageref{LastPage}} %% \fancyhead[RO]{\slshape \thepage/ \pageref{LastPage}} %\fancyhf{} %\fancyhead[LE]{\slshape LE} %\fancyhead[CE]{\slshape CE} %\fancyhead[RE]{\slshape RE} \fancyhead[LO]{\bfseries\rightmark} %\fancyhead[CO]{\slshape APF} \fancyhead[RO]{\bfseries\leftmark} %% %\fancyfoot{} % \fancyfoot[LE,RO]{} \fancyfoot[CO,CE]{\slshape\thepage/\pageref{LastPage}} %% %\fancyfoot[LO,RE]{\small\slshape \ddmmyyyydate version du \today} } % \pagestyle{fancy} \pagestyle{monstyle} \newcommand{\code}[1]{\texttt{#1}} \usepackage{boxedminipage} \newsavebox\svbx \newif\ifcache \long\def\cache#1{\ \newline \setbox\svbx=\vbox{\leavevmode \newline \begin{spacing}{1}#1\end{spacing}} \smallskip\par\noindent \begin{boxedminipage}{\linewidth} \ifcache \leavevmode\hrule height 0pt\vskip \ht\svbx\hrule height 0pt \else \unvbox\svbx \fi \end{boxedminipage} \par\smallskip} %\cachefalse % version prof \cachetrue % version etudiant \makeindex \begin{document} \begin{titlepage} \begin{center} \textsc{\Large IUT Informatique Aubière \hfill 2019 - 2020} \\[.5cm] \hrule \ \\[.5cm] \vfill \textsc{\LARGE Bases de données} \vfill \textsc{\LARGE \proc{}} \vfill {\Large Pascale \textsc{Brigoulet}, Franck \textsc{Glaziou}, } {\Large Pascal \textsc{Lafourcade} et Marie-Fran\c{c}oise \textsc{Servajean}} \vfill \begin{tikzpicture} %%% un triangle %% horizonatale \draw[blue,line width=1pt] (-1,0) -- (1,0); \draw[blue,line width=1pt] (-2.5,-.875) -- (1.5,-.875); \draw[blue,line width=1pt] (-1.5,-1.75) -- (3,-1.75); \draw[blue,line width=1pt] (0,3.5) -- (1,3.5); %% Croissante \draw[blue,line width=1pt] (0,0) -- (1,1.75); \draw[blue,line width=1pt] (-2.5,-.875) -- (0,3.5); \draw[blue,line width=1pt] (-1,0) -- (1,3.5); \draw[blue,line width=1pt] (3,-1.75) -- (3.5,-0.75); %% Decroissante \draw[blue,line width=1pt] (0.5,.875) -- (1.5,-.875); \draw[blue,line width=1pt] (1,1.75) -- (3,-1.75); \draw[blue,line width=1pt] (1,3.5) -- (3.5,-0.75); \draw[blue,line width=1pt] (-2.5,-.875) -- (-1.5,-1.75); \end{tikzpicture} %% \begin{tikzpicture} %% %%% un cube %% \draw[red,line width=1pt] (0,.5) -- (1,0); %% \draw[red,line width=1pt] (2,.5) -- (1,0); %% \draw[red,line width=1pt] (0,1.5) -- (1,1); %% %\draw[red,line width=1pt] (0,1.5) -- (1,2); %% \draw[red,line width=1pt] (0,1.5) -- (.5,1.75); %% %\draw[red,line width=1pt] (2,1.5) -- (1,2); %% \draw[red,line width=1pt] (2,1.5) -- (1.5,1.75); %% \draw[red,line width=1pt] (2,1.5) -- (1,1); %% \draw[red,line width=1pt] (1,1) -- (1,0); %% \draw[red,line width=1pt] (0,1.5) -- (0,.5); %% \draw[red,line width=1pt] (2,1.5) -- (2,.5); %% %% droite %% %\draw[red,line width=1pt] (1.5,1.25) -- (2.5,0.75); %% \draw[red,line width=1pt] (2,1) -- (2.5,0.75); %% \draw[red,line width=1pt] (3.5,1.25) -- (2.5,0.75); %% \draw[red,line width=1pt] (1.5,2.25) -- (2.5,1.75); %% \draw[red,line width=1pt] (1.5,2.25) -- (2.5,2.75); %% \draw[red,line width=1pt] (3.5,2.25) -- (2.5,2.75); %% \draw[red,line width=1pt] (3.5,2.25) -- (2.5,1.75); %% \draw[red,line width=1pt] (2.5,1.75) -- (2.5,.75); %% %\draw[red,line width=1pt] (1.5,2.25) -- (1.5,1.25); %% \draw[red,line width=1pt] (1.5,2.25) -- (1.5,1.75); %% \draw[red,line width=1pt] (3.5,2.25) -- (3.5,1.25); %% %% Haut %% \draw[red,line width=1pt] (0,2) -- (1,1.5); %% %\draw[red,line width=1pt] (2,2) -- (1,1.5); %% \draw[red,line width=1pt] (1.5,1.75) -- (1,1.5); %% \draw[red,line width=1pt] (0,3) -- (1,2.5); %% \draw[red,line width=1pt] (0,3) -- (1,3.5); %% \draw[red,line width=1pt] (2,3) -- (1,3.5); %% \draw[red,line width=1pt] (2,3) -- (1,2.5); %% \draw[red,line width=1pt] (1,2.5) -- (1,1.5); %% \draw[red,line width=1pt] (0,3) -- (0,2); %% %\draw[red,line width=1pt] (2,3) -- (2,2); %% \draw[red,line width=1pt] (2,3) -- (2,2.5); %% \end{tikzpicture} \vfill \includegraphics[width=5cm]{iut-uca.png} \end{center} \vfill {\Large Nom : \\ Prénom : \\ Groupe : \\ } %\url{http://mocodo.wingi.net/} %\url{http://mirror.hmc.edu/ctan/graphics/pgf/contrib/tkz-orm/tkz-orm.pdf} \end{titlepage} %% \section*{Avant Propos} %% L’objectif de ce cours de base de données avancées est de %% présenter %% \newpage \tableofcontents \newpage \section*{Interface \proc{}} \proc{} est une interface entre le langage C et le SGBD Oracle. Il s'agit d'une interface de précompilation, c'est-à-dire que le programmeur écrit ses ordres SQL dans son code C et ceux-ci sont traduits par un précompilateur. Le rôle du précompilateur est de traduire un programme comprenant des commandes SQL en un programme ne comprenant que des instructions du langage C et pouvant accéder à la base de données (voir Figure~\ref{fig:proc}). Il s'agit de remplacer les commandes SQL par des appels à des modules Oracle combinant ainsi les avantages d'un langage procédural C à ceux d'un langage non procédural SQL. \begin{figure}[htb] \begin{center} \includegraphics[width=12cm]{proc.png} \end{center} \caption{Illustration du processus permettant d'utiliser un fichier \proc{}}\label{fig:proc} \end{figure} La précompilation, la compilation et l'édition de liens de tels programmes sont masquées par l’exécution d’une commande sur les fichiers sources qui doivent être suffixés par « \code{.pc} ». \section{Structure d’un programme \proc{}} Un fichier \proc{} se structure de la même manière qu’un fichier C. En plus des commandes SQL, il faut simplement penser à la connexion et la déconnexion à la base de données. Il est donc important de préparer la liaison entre C et Oracle. Pour cela il faut déclarer un ensemble de variables de communication et une zone de communication pour récupérer les comptes rendus Oracle. \subsection{La section \code{INCLUDE} : (même fonction que le include du C).} Le fichier \code{SQLCA} permet de connaître le résultat d'une commande \proc{} (erreur, nombre de lignes résultant de la requête, ...). La syntaxe est la suivante : \code{EXEC SQL INCLUDE SQLCA.H;} \subsection{La déclaration de variables} La déclaration des variables hôtes, c'est-à-dire les variables utilisées dans les commandes SQL, s’effectue exactement de la même manière qu’en C. \begin{verbatim} int pemno; VARCHAR pname[11]; int pdeptno; \end{verbatim} Il est important de noter qu’une variable hôte : \begin{itemize} \item doit être précédée de '\code{:}' lorsqu'elle est utilisée (sauf lors de la déclaration), \item ne doit pas être un mot réservé SQL. \end{itemize} \proc{} permet l'utilisation du type \code{VARCHAR} pour travailler avec les chaînes de caractères de longueur variable. C’est le seul type SQL qui peut être utilisé. Il est important de toujours choisir avec attention les types C car ils vont contenir les informations issues des types SQL. Par exemple, \code{NUMBER(8,2)} ne peut pas tenir dans un \code{int}. Il est important de noter que chaque variable de type \code{VARCHAR} va être traduite par le précompilateur par une structure C. \code{VARCHAR poste[40];} Le code ci-dessus va être traduit ainsi : \begin{verbatim} struct { unsigned int len; /* longueur de la chaîne */ unsigned char arr[40]; /* la chaîne elle-même */ } poste ; \end{verbatim} L’utilisation de \code{VARCHAR} permet donc de s’abstraire de la gestion du \verb+‘\0’+ qui n’est pas reconnu par Oracle. Ainsi la longueur de la chaîne est celle située dans l'entier \code{len}. En sortie d'une commande, Oracle complète la chaîne avec des espaces à droite et met à jour la longueur. Pour entrer une commande, il faut faire attention \`a cela. \subsection{La connexion à la base de données} Le plus simple est souvent de définir une fonction \`a réutiliser à chaque fois. \begin{verbatim} void connexion() { VARCHAR uid[50]; char login[20]; char passwd[20]; printf("Donner votre login : "); scanf("%s",login); printf("\nDonnez votre mot de passe Oracle : "); scanf("%s",passwd); printf("\n"); strcpy(uid.arr,login); strcat(uid.arr,"/"); strcat(uid.arr,passwd); strcat(uid.arr,"@kirov"); uid.len=strlen(uid.arr); EXEC SQL CONNECT :uid; if (sqlca.sqlcode==0) printf(" Connexion réussie avec succès.\n\n"); else { printf ("Problème à la connexion.\n\n"); exit(1); } } \end{verbatim} Pour la déconnexion, le même principe est à utiliser. Cela permet d’appeler la fonction déclarée à chaque fois que nécessaire. Il est possible de pr\'evoir le cas où il faut valider les transactions et les cas où il faut les annuler. \begin{verbatim} void deconnexion(int validation) { if (validation == 1) { EXEC SQL COMMIT WORK RELEASE; } else { EXEC SQL ROLLBACK WORK RELEASE; } printf("Déconnexion sans problème.\n"); } \end{verbatim} \subsection{Le corps de l'application} Il contient essentiellement des ordres SQL aussi bien pour la manipulation de données que pour la définition des données. La syntaxe utilisée est la même que celle de PL/SQL avec en plus l'utilisation possible de variables hôtes partout où une constante peut être employée (nom de relation, nom d'attribut,...). \begin{verbatim} EXEC SQL CREATE TABLE empTest(empno NUMBER(2)); EXC SQL INSERT INTO tligne VALUES('exception produit inexistant'); EXEC SQL SELECT job INTO :function FROM emp WHERE noemp=301; EXEC SQL UPDATE emp SET sal = :sal WHERE noemp = :noemp; EXEC SQL DELETE FROM emp WHERE noemp = :noemp; \end{verbatim} Toutes les sortes de requêtes peuvent être utilisées. Les requêtes \code{SELECT} suivent exactement les mêmes règles qu’en PL/SQL. Dans l’exemple précédent, le \code{SELECT} doit retourner une et une seule ligne. L’utilisation des curseurs pour les multi-lignes est présentée après. \begin{exercice}~ \begin{enumerate} \item \'Ecrire un programme Pro-C qui se connecte à Kirov puis affiche le nombre de produits contenus dans la base de données, puis se déconnecte. \begin{SaveVerbatim}{countprod} int main(void) { int nbProd; EXEC SQL WHENEVER SQLERROR DO sql.error(``Oracle error \n''); connexion(); EXEC SQL SELECT COUNT(*) INTO :nbProd FROM TPRODUIT; printf("Il y a %d produits en base.\n", nbProd); deconnexion(); } \end{SaveVerbatim} \cache{\tiny \BUseVerbatim{countprod}} \item \'Ecrire un programme Pro-C qui demande à l’utilisateur un produit et l’ajoute à la BD. \begin{SaveVerbatim}{addprod} void q2(void) { char codeRayon[REFERENCE_LEN], dateStock[DATE_LEN], nCodeRayon[REFERENCE_LEN], nDateStock[DATE_LEN], noProd[REFERENCE_LEN]; int choix, nStock, stock; float nPrixV, prixV; VARCHAR des[VARCHAR_LEN], nDes[VARCHAR_LEN]; printf("Saisir numéro produit : "); scanf("%s%*c", noProd); EXEC SQL SELECT des, stock, prixV, codeRayon, dateStock INTO :des, :stock, :prixV, :codeRayon, :dateStock FROM TPRODUIT WHERE noProd = :noProd; printf("1 - Désignation : %.*s.\n", des.len, des.arr); printf("2 - Stock : %d.\n", stock); printf("3 - Prix de vente : %.2f euro.\n", prixV); printf("4 - Code rayon : %s.\n", codeRayon); printf("5 - Date : %s.\n", dateStock); while (choix != 9) { printf("Que voulez-vous modifier (9: quitter) ?\n"); scanf("%d%*c", &choix); switch (choix) { case 1: printf("Entrez la nouvelle désignation : "); fgets(nDes, sizeof nDes, stdin); if (nDes[strlen(nDes) - 1] == '\n') { nDes[strlen(nDes) - 1] = '\0'; } EXEC SQL UPDATE TPRODUIT SET des = :nDes WHERE noProd = :noProd; if (sqlca.sqlcode < SQL_SUCCESS) {erreur_sql(STOP);} break; case 2: printf("Entrez le nouveau stock : "); scanf("%d%*c", &nStock); EXEC SQL UPDATE TPRODUIT SET stock = :nStock WHERE noProd = :noProd; if (sqlca.sqlcode < SQL_SUCCESS) {erreur_sql(STOP);} break; case 3: printf("Entrez le nouveau prix : "); scanf("%f%*c", &nPrixV); EXEC SQL UPDATE TPRODUIT SET prixV = :nPrixV WHERE noProd = :noProd; if (sqlca.sqlcode < SQL_SUCCESS) {erreur_sql(STOP);} break; case 4: printf("Entrez le nouveau code rayon : "); scanf("%s", &nCodeRayon); EXEC SQL UPDATE TPRODUIT SET codeRayon = :nCodeRayon WHERE noProd = :noProd; if (sqlca.sqlcode < SQL_SUCCESS) {erreur_sql(STOP);} break; case 5: printf("Entrez la date : "); scanf("%s", &nDateStock); EXEC SQL UPDATE TPRODUIT SET dateStock = :nDateStock WHERE noProd = :noProd; if (sqlca.sqlcode < SQL_SUCCESS) {erreur_sql(STOP);} break; default: printf("Choix inconnu.\n"); break; } EXEC SQL COMMIT;}} \end{SaveVerbatim} \cache{\scriptsize \BUseVerbatim{addprod}} \end{enumerate} \end{exercice} \subsection{Le traitement des erreurs} \subsubsection{Le fichier \code{SQLCA.H}} Il définit une structure de données '\code{sqlca}' dont les champs sont mis à jour après chaque exécution d'un ordre SQL et qui en donne le compte rendu. \begin{verbatim} struct { long sqlcode; /* code resultant de l'exécution =0 -> ok, >0 -> ok avec un code d'état, <0 -> erreur */ struct { unsigned short sqlerrml; /*longueur du message*/ char sqlerrmc[70]; /*message d'erreur*/ } sqlerrm; long sqlerrd[6]; /* seul sqlerrd[2] est utilisé -> donne le nombre de lignes modifiées UPDATE ou rajoutées par INSERT ou ramenées par un SELECT*/ char sqlwarn[8]; /*sqlwarn[0] 'W’ -> warning*/ sqlwarn[0] = '' /*-> pas de warning*/ sqlwarn[1] = 'W' /*-> troncation numérique ou char*/ sqlwarn[2] = 'W' /*-> valeur Null est ignore*/ sqlwarn[3] = 'W' /*-> plus de champs dans SELECT que de variables pour recevoir*/ sqlwarn[4] = 'W' /*-> toutes les lignes d'une table sont touchées (par DELETE ou UPDATE par exemple)*/ sqlwarn[5] /*inutilisé*/ sqlwarn[6] = 'W' /*-> Oracle a dû exécuter un rollback*/ sqlwarn[7] = 'W' /*-> la donnée ramenée par un FETCH a été modifié depuis que la clause SELECT a été execute*/ } sqlca; \end{verbatim} Après chaque commande SQL il est possible de tester le champ de \code{sqlca} correspondant à une erreur possible dans le cadre de cette commande. \subsection{La commande \code{WHENEVER}} Oracle permet d'utiliser des directives qui spécifient le traitement à effectuer en cas d'erreur. \begin{verbatim} EXEC SQL WHENEVER [SQLERROR|SQLWARNING|NOT FOUND][STOP|CONTINUE|GOTO