I. Introduction

Cet article va vous apprendre à utiliser les bases de données avec PureBasic, et principalement SQLite. L'interface graphique et le code le gérant ne seront pas expliqués.

Mais qu'est ce donc que SQLite ?
SQLite est un gestionnaire de base de données sans serveur, utilisant un fichier comme stockage. Il n'y a rien à déployer ou à configurer, il est tout de suite opérationnel. SQLite est largement utilisé dans l'industrie et est considéré comme un des meilleurs gestionnaire de base de données embarqué. Pour plus d'informations: http://www.sqlite.org. Voici quelques une des caractéristiques de SQLite :

II. Pré-Requis

Le pré-requis principal est de savoir utiliser les fenêtres sous PureBasic, vu que cette partie du code ne vous sera pas expliquée. Le second pré-requis est de connaître les principes des bases de données. En cela, voici quelques articles intéressants :

III. Principe

En SQL, il existe quatre types de requêtes principales : DELETE, INSERT, SELECT, UPDATE.

Une requête de type SELECT permet de faire une SELECT-ion dans une ou plusieurs bases de données, en définissant un à plusieurs critères.
Une requête de type INSERT permet de faire une INSERT-ion de lignes dans une ou plusieurs bases de données.
Une requête de type UPDATE permet de mettre à jour un ou plusieurs champs dans une à plusieurs lignes d'une base de données.
Une requête de type DELETE permet de supprimer une à plusieurs lignes dans une base de données en fonction d'un à plusieurs critères.

Au final, SELECT est le seul type de requêtes à renvoyer des données car les trois autres ne font que des mises à jour internes aux bases de données. A partir de cela, deux fonctions PureBasic sont intéressantes : DatabaseQuery et DatabaseUpdate. DatabaseQuery sera utilisé avec les requêtes de type SELECT. DatabaseUpdate sera utilisé avec les requêtes de type DELETE, INSERT et UPDATE.

IV. L'interface graphique

Même sans aller en profondeur dans l'étude de la GUI, je vais vous présenter l'interface graphique. Elle est basée sur une fenêtre principale qui chargera les informations du carnet d'adresses et du contact sélectionné. Le chargement initial se fera via la fonction DB_Init().Une listview liste tous les contacts et en fonction de la sélection, l'application appellera DB_SelectContact(). Quatre boutons accompagnent cette interface :

  • Le bouton "Reset" permettra de réinitialiser le contenu des StringGadget.
  • Le bouton "Ajouter" permettra d'insérer un nouveau contact. L'application appellera la fonction DB_InsertContact().
  • Le bouton "Mettre à jour" permettra de mettre à jour le contact courant. L'application appellera la fonction DB_UpdateContact().
  • Le bouton "Supprimer" permettra d'effacer le contact courant. L'application appellera la fonction DB_DeleteContact().

Sous Ubuntu 8.04 :

GUI Linux

Sous Windows XP :

GUI Windows

V. Les fonctions

V-A. DB_Init()

On initialise l'accès aux bases de données SQLite via la fonction UseSQLiteDatabase().

Ensuite, on teste l'existence du fichier SQLite et en cas de non-présence, on crée le fichier.

 
CacherSélectionnez
If FileSize(#PB_Compiler_FilePath+"GestionContacts.sqlite") < 0
    plFile = CreateFile(#PB_Any, #PB_Compiler_FilePath+"GestionContacts.sqlite")
    If plFile
      CloseFile(plFile)
    EndIf
  EndIf

Ensuite, on se connecte à la base via OpenDatabase(). Le premier paramètre est l'identifiant. Le second est le chemin vers la base de données SQLite. Les troisième et quatrième paramètres sont des champs vides car SQLite ne gère pas les connexions avec des utilisateurs. Le cinquième paramètre est le plugin : #PB_Database_SQLite pour les bases SQLite.

 
CacherSélectionnez
glDBSQLite = OpenDatabase(#PB_Any, #PB_Compiler_FilePath+"GestionContacts.sqlite", "", "", #PB_Database_SQLite)
  If glDBSQLite = 0
    MessageRequester("DVP - Gestion de Contacts", DatabaseError())
    End
  EndIf

Ensuite, si le fichier vient d'être créé, on crée la table. Il suffit de faire une requête CREATE TABLE et vu qu'il n'y aucun retour de données comme les requêtes SELECT, on utilise la fonction DatabaseUpdate().

 
CacherSélectionnez
If plFile
    psSQLRequest = "CREATE TABLE IF NOT EXISTS contacts ("
    psSQLRequest + "id_contact INTEGER PRIMARY KEY AUTOINCREMENT Not NULL, "
    psSQLRequest + "contact_nom TEXT Not NULL, "
    psSQLRequest + "contact_prenom TEXT Not NULL, "
    psSQLRequest + "contact_job TEXT, "
    psSQLRequest + "contact_company TEXT, "
    psSQLRequest + "contact_address TEXT, "
    psSQLRequest + "contact_postalcode TEXT, "
    psSQLRequest + "contact_city TEXT, "
    psSQLRequest + "contact_country TEXT, "
    psSQLRequest + "contact_phone TEXT, "
    psSQLRequest + "contact_fax TEXT, "
    psSQLRequest + "contact_mail TEXT, "
    psSQLRequest + "contact_photo BLOB, "
    psSQLRequest + "contact_photo_size INTEGER"
    psSQLRequest + ")"
    ; Debug psSQLRequest
    If DatabaseUpdate(glDBSQLite, psSQLRequest) = 0
      MessageRequester("DVP - Gestion de Contacts - DB_Init()", DatabaseError())
    EndIf
  EndIf

Pour finir, on charge le contenu de la base - simplement l'identifiant, le nom et le prénom du contact - dans la listview. Contrairement aux requêtes sans retour de données, on n'utilise pas DatabaseUpdate(), on utilise DatabaseQuery(). Dés lors, une boucle While... Wend avec un NextDatabaseRow() permet de parcourir les enregistrements retournés par le SELECT. Pour récupérer le contenu d'une colonne dans un enregistrement, il faut les GetDatabase* où * correspond au type : String, Float, Long, etc... Ces fonctions utilisent deux paramètres : l'un est l'identifiant de la base et l'autre, le numéro de la colonne (la première colonne étant la colonne 0).

 
CacherSélectionnez
If DatabaseQuery(glDBSQLite, "SELECT * FROM contacts") >< 0
    While NextDatabaseRow(glDBSQLite)
      AddGadgetItem(#Listview_0, CountGadgetItems(#Listview_0), GetDatabaseString(glDBSQLite, 1) + " " + GetDatabaseString(glDBSQLite, 2))
      SetGadgetItemData(#Listview_0, CountGadgetItems(#Listview_0)-1, GetDatabaseLong(glDBSQLite, 0))
    Wend
    FinishDatabaseQuery(glDBSQLite)
  EndIf

Code Complet :

 
CacherSélectionnez

V-B. DB_DeleteContact()

Une fonction sans retour telle qu'une requête DELETE sera utilisée avec la fonction DatabaseUpdate(). Si la fonction DatabaseUpdate() retourne 0, alors il y a eu un problème. Dans ce cas-là, il est possible d'utiliser la fonction DatabaseError(). Cette fonction vous permettra de récupérer la dernière erreur récupérée lors de l'exécution d'un requête.

Mais si la fonction DatabaseUpdate() s'exécute correctement, on efface l'élément sélectionné de la ListView.

Code Complet :

 
CacherSélectionnez

V-C. DB_InsertContact()

On utilise toujours DatabaseUpdate() car une requête INSERT ne retourne aucun enregistrement.

Mais au niveau de l'interface graphique, on a besoin de faire une requête SELECT. Pourquoi ? Car comme précisé ci-dessus, une requête INSERT ne retourne aucun enregistrement et par conséquent, il est impossible de connaître l'ID du contact associé lors de la requête INSERT.

Ce code est assez simple. On exécute la requête INSERT. Si cela ne passe pas bien, on affiche une boîte de dialogue avec l'erreur. Sinon on lance une requête SELECT afin de récupérer le dernier enregistrement inséré dans la table CONTACTS. Pour cela, il suffit d'utiliser la procédure stockée MAX qui permet de ne garder que l'élément maximal de la colonne précisée. Ainsi, on ajoute le nom et le prénom de l'enregistrement sélectionné dans la ListView auquel on associe via SetGadgetItemData() l'identifiant du contact.

Il me reste à vous expliquer l'insertion de blobs. Mais qu'est ce qu'un blob ? C'est un type de donnée permettant le stockage de données binaires. Dans notre cas, ce sera des images. Pour cela, on va utiliser la fonction SetDatabaseBlob(). Cette fonction remplace dans la requête lancée par DatabaseUpdate() ou DatabaseQuery() le '?' par une zone mémoire. Donc dans SetDatabaseBlob(), les paramètres sont les suivants :

  • #Database : l'identifiant de la base de données
  • StatementIndex.l : l'index du blob dans la requête (de 0 à n-1 blobs dans la requête)
  • *Buffer : la zone mémoire
  • BufferLength : la taille de la zone mémoire

Il est conseillé de créer un champ qui stockera la taille de la zone mémoire.

Code Complet :

 
CacherSélectionnez

V-D. DB_SelectContact()

Vu que cette commande récupère les informations du contact, on a besoin d'utiliser une requête SELECT. Et par conséquent, on a besoin d'un enregistrement donc de la fonction DatabaseQuery().

Si la fonction DatabaseQuery() retourne 0, il y a une erreur. Sinon, une boucle While... Wend permettra de récupérer les enregistrements. Pour passer d'un enregistrement à l'autre, il suffit d'utiliser la fonction NextDatabaseRow(). Ainsi, grâce à la boucle While... Wend, dès que la fonction NextDatabaseRow() retournera 0, ce qui veut dire qu'il n'y a plus d'enregistrements, le programme sortira de la boucle principale.

Dés lors, il nous reste une fonction à utiliser : FinishDatabaseQuery(). Cette fonction permet de clore une fonction DatabaseQuery() et de libérer les ressources associées. Par conséquent, il n'est plus possible d'utiliser les fonctions FirstDatabaseRow() et NextDatabaseRow().

Grâce au dernier champ qui contient la taille de la zone mémoire, on alloue celle-ci. Ensuite, grâce à GetDatabaseBlob(), on choisit quelle colonne d'où extraire la zone mémoire via le deuxième paramètre. Les deux derniers paramètres permettent de configurer la zone mémoire.

Code Complet :

 
CacherSélectionnez

V-E. DB_UpdateContact()

On retourne à l'utilisation d'une requête UPDATE. Donc par conséquent, on utilise la fonction DatabaseUpdate(). Comme toujours, en cas d'erreur, on peut récupérer l'erreur via la fonction DatabaseError().

Côté interface graphique, un simple SetGadgetItemText() permet de modifier le nom et le prénom de l'élément sélectionné dans la ListView.

Code Complet :

 
CacherSélectionnez

VI. Compilation et Exemple

Téléchargez la source du tutoriel :
Source du tutorielSource du tutoriel
Par défaut, toutes les options sont enregistrées dans le fichier.
Compilez votre fichier et automatiquement, le programme devrait être fonctionnel.

VII. Conclusion

Voilà, nous arrivons à la fin de cet article. L'avantage est que ce code est fonctionnel sous Windows (testé sous Windows XP), Linux (testé sous Ubuntu 8.04) et sûrement sous Mac Os.
Ce code est facilement améliorable et tout cela avec des petites idées simples

  • Gérer l'import/export de fichiers CSV, Texte, VCard (format standard de gestion de contacts)
  • Utiliser ODBC ou PostGreSQL pour se connecter à d'autres types de bases
  • Crypter les données
  • Gérer d'autres informations ou données sur le contact
  • Pouvoir associer des fichiers externes avec un contact

J'espère que cet article permettra aux gens d'avoir un nouveau point de vue sur PureBasic.
Et maintenant à vos claviers !
N'oubliez pas les forums dédiés à SQLite et PureBasic.

VIII. Remerciements

Merci à ComtoisRédacteur PureBasic pour la relecture.
Enfin je dis un grand merci à l'équipe de DVP pour leur travail.