Base de données Nouvelles notions Voici quelques nouvelles fonctionnalités du langage dont vous aurez à vous servir. Prenez le temps de bien comprendre et tester les exemples. Tableaux à taille dynamique Vous n êtes pas obligé de figer la taille d un tableau. Par exemple, on souhaite disposer d un tableau dont on fera varier la taille au fil du temps. Dans ce cas, on va le declarer sans spécifier d intervalle : var tab_dyn : array of integer; Ensuite, on peut utiliser les fonctions length et setlength pour respectivement lire et changer la taille du tableau. setlength(tab_dyn, 2); tab_dyn[0] := 4; tab_dyn[1] := 2; setlength(tab_dyn, length(tab_dyn) + 1); tab_dyn[2] := 8; setlength(tab_dyn, 2); Cet exemple illustre l utilisation des tableaux dynamiques. Notez bien que l indice de départ est 0. De plus, lorsqu on redimensionne un tableau avec setlength, les données encore accessibles sont conservées. Dans l exemple ci-dessus, à la fin le tableau contient toujours 4 et 2 dans ses deux premières cases. Par contre la valeur 8 a été effacée. Enumérations L énumération est un mécanisme qui permet de passer des valeurs prédéfinies constantes comme par exemple des options. Une énumération fait correspondre à un nom une valeur (que le programmeur ignore, il n en a pas besoin). Cela ressemble un peu aux types sommes (dans leur forme la plus simple!) Voici l analogie entre Caml et Delphi : (* en Caml *) # type couleur = Bleu Blanc Rouge Noir;; let test_couleur color = match color with Bleu Blanc -> print_string( Bleu ou blanc ) Rouge -> print_string( Rouge ) _ -> print_string( probablement noir );; (* en Pascal *) type TCouleur = (Bleu, Blanc, Rouge, Noir); 1
procedure test_couleur(color : TCouleur); begin case color of Bleu: Blanc: writeln( Bleu ou blanc ); Rouge: writeln( Rouge ); else writeln( probablement noir ); Enregistrements à parties variables Les enregistrements à partie variable permettent de n utiliser certains champs que sous certaines conditions. Par exemple, reprenons le type TPersonne du précédent TP : type TPersonne = record nom, prenom : string; age : integer; case fils_unique : boolean of false: (nb_freres, nb_soeurs : integer); true: (); Les champs nb freres et nb soeurs n ont de sens que si l a personne n est pas fils unique (donc si ce champs mis à faux). Dans le cas contraire (fils unique mis à vrai), alors ces champs ne peuvent pas être accedés. Un autre exemple, qui pourrait bien vous être utile par la suite... type TStringOrInteger = record case is_string : boolean of true: (s : string); false: (i : integer); Base de données Nous vous demandons pour ce TP de construire vous même les types que vous utiliserez. Il vous est donc fortement conseillé de lire en entier le sujet avant de commencer et de regarder le code fourni. Types requis Des valeurs... Une donnée stockée dans la base peut être soit un entier, soit une chaîne de caractères, soit un nombre à virgule. Vous allez donc pouvoir utiliser une énumération pour indiquer le type, et faire un enregistrement à partie variable pour n utiliser que la donnée qui nous interesse (celle dont on a precisé le champ type). Ce type se nommera TValue. Exemple : 2
var v : TValue; v.ty := ty_int; v.value_int := 3; v.ty := ty_str; v.value_str := test ; Une base de données Une base de données sera composée d un ensemble de tables. Vous devez ecrire le type TDB correspondant. Des tables Les données seront contenues dans ce qu on appelle une table (type TTable). Chaque table d une base dispose d un nom unique. Une table se présente sous forme d un tableau à deux dimensions : les colonnes seront appelées champs et les lignes enregistrements. Une table est donc : Une liste de champs (TField), indiquant pour chaque colonne son nom, son type et une valeur par défaut. Les champs ne contiennent pas de données. Une liste d enregistrements (TRecord), qui indique pour chaque enregistrement la valeur de chaque champ (souvenez-vous des tableaux à deux dimensions). STOP! Une fois à ce point du TP, montrez votre travail aux ACD! De mauvaises structures ne vous permettront pas de continuer le TP. Opérations à implémenter Créer une table Avant de commencer à ajouter des données, il va d abord falloir créer de quoi les contenir! Vous devez donc ecrire une fonction qui ajoute une nouvelle table dans une base de données, en précisant les différents champs, leur noms, types et valeurs par défaut. function add_table(var db : TDB; name : string; fields : array of TField) : boolean; Ajoute une table nommée name dans la base db Si la table existe déjà, retourne faux sans rien faire. La table créee contiendra les champs fields. piste : vous pouvez utiliser length et setlength pour redimensionner le tableau de tables. Vous aller ainsi créer un emplacement libre à la fin du tableau. Il vous reste à le remplir... Ajouter des enregistrements A partir de maintenant, nous allons commencer à manipuler vraiment les données de notre base. function insert_into(var db : TDB; table : string; rec : TData) : boolean; 3
Ajoute dans la table table de la base db les données data. Si une erreur survient (nom de table inconnu ou données invalides), retourner faux. La structure TData sera la suivante : TData = array of TSingleData; TSingleData = record field : string; value : TValue; Des données sont donc une liste de couples champs + valeur. Attention! lorsque vous ajouter un enregistrement, vous devez remplir TOUS les champs, meme si l utilisateur n en spécifie pas dans rec. Dans ce cas, utilisez la valeur par défaut du champs. piste : après avoir localisé la bonne table, vous pouvez appliquer le même principe que pour la question précédente. Attention tout de même aux tableaux bidimensionnels! Selectionner des enregistrements Dans cette partie, nous allons extraire des données selon certains critères. Par exemple, si nous avons une base de données d étudiants, nous souhaiterions pouvoir recuperer l enregistrement d un élève suivant son nom, ou bien encore plusieurs élèves selon leur classe. function select_from(db : TDB; table : string; where : TWhere; var rs : TRecordset) : integer; Recherche dans la table table de la base db les données respectant la clause where. Les stoque dans rs. La fonction retourne le nombre d enregistrements contenus dans rs. -1 en cas d erreur. Le type TWhere est simplement défini de la manière : TWhere = TSingleData; Pour l instant, on ne vous demande de ne gerer qu une seule condition (ne recuperer que les enregistrements ou le champ where.field à la valeur where.value). Le type pour les données retourné est TRecordset : TRecordset = array of array of TSingleData; En effet, vous recuperez une liste d enregistrements (plusieurs peuvent repondre au critère indiqué), et un enregistrement est une liste de couples nom + valeur. STOP! Avant de continuer, vous devez vous assurer que les 3 opérations ci-dessus marchent correctement! Les ACD vous ont fournit quelques tests, mais c est à vous d en faire d autres plus poussés! Modifier des enregistrements Une base de données figée ne servirait pas à grand chose. Il faut qu on puisse modifier des données ajoutées précédement. function update(var db : TDB; table : string; where : TWhere; new : TSingleData) : boolean; Modifier la table table de la base db, en changeant pour tous les enregistrements respectant la condition where le champs new.field avec comme nouvelle valeur new.value. En cas d erreur, on retourne false. N oubliez pas que cette fonction peut modifier plusieurs enregistrements (pas seulement le premier qui respecte la condition). 4
Supprimer des enregistrements On arrive à la dernière opération indispensable : la suppression. Il vous est laissé le choix au niveau de l implémentation pour le reclassement : vous pouvez prendre par exemple le dernier enregistrement et le mettre à la place de celui supprimé, ou alors décaler tous les enregistrements à la suite de celui supprimé ou encore toute autre méthode qui fonctionne. function delete_from(var db : TDB; table : string; where : TWhere) : boolean; Supprime de la table table les enregistrements qui suivent la condition where. En cas d erreur (table inexistante), on retourne false. Même remarque que précédement : on supprime TOUS les enregistrements possible qui respectent where. Améliorations Voici quelques suggestions si vous avez fini en avance. Selection selon plusieurs critères Difficulté : facile Vous devez modifier la fonction de sélection d enregistrements de manière à ce qu on puisse donner plusieurs critères (changer TWhere en un tableau plutôt qu un unique couple champs + valeur). Critères avancés Difficulté : moyen Vous devez permettre de passer des critères plus complexe qu une simple contrainte d égalité des valeurs. Nous entendons par la modifier TWhere de manière à pouvoir par exemple selectionner tous les étudiants dont la promotion est supérieure, inférieur ou égale à 2008 (jusqu à maintenant, vous ne geriez que le égal ). Application Difficulté : moyen Vous pouvez porter les applications du précédent TP (carnet d adresse et CDDB) en utilisant votre nouveau système de base de données. SQL Difficulté : difficile Si vous vous sentez vraiment de le faire, pourquoi ne pas implémenter un interpreteur de requêtes SQL! (http ://fr.wikipedia.org/wiki/sql) 5