/* * Exemple d'utilisation de la libriairie 'libsqlite' pour Scol * * Cet exemple a été réalisé avec la version 0.1a1 * Auteur : Stéphane Bisaro, aka Iri * Juillet 2010 * Licence : aucune licence particulière, ce code source peut être modifié, * distribué, étudié et exécuté librement, sans restriction. * Notez cependant qu'il peut contenir des dysfonctionnements ou du code erroné. * Il est fourni tel quel sans garantie d'aucune sorte. Prenez toute précuation * d'usage. * La librairie 'lisqlite' est, elle, placée sous licence Scol. * * Ce code source montre les principes de bases de l'utilisation de 'libsqlite'. * Le but n'est pas une optimisation tant du code Scol que des requêtes SQL. * Cet exemple crée s'il y a lieu un fichier base de données sur le disque dur * pour y tester diverses commandes SQL via une interface graphique. * * Si vous avez des questions à propos de ce code, privilégiez les forums du * Scolring : http://www.scolring.org */ var PATH = "tutorials/sqlite/";; var BASENAME = "test_1.database";; typeof tlog = ObjText;; // Objet connexion à la base de donnée typeof db = ObjSqlite;; proto dbClose = fun [] I;; /* Fonctions générales Destruction de l'application si et seulement si la connexion à la base est close */ fun end()= // Nous détruisons la machine virtuelle que si la fermeture de la connexion à la // base de données est correcte if (!dbClose) then _closemachine else 0;; fun write(s)= _ADDtext tlog strcat "\n" s; 0;; /* Fonctions relatives à la base de données SQLite peut créer le fichier base de données s'il n'existe pas lors de la connexion. Cependant, la fonction Scol ne le fait pas, il est alors nécessaire de s'assurer de sa présence préalable et de le créer le cas échéant. C'est ce que réalise la fonction dbCreate. Bien entendu, ce fichier DOIT se trouver dans la partition active de Scol et être accessible en lecture a minima. Pour ouvrir une connexion à la base (dbOpen), nous avons le choix entre quatre fonctions : - _sqliteOpenFile est la version simplifiée pour une base fichier stanadard - _sqliteOpenFileEx est la version complète pour une base fichier standard - _sqliteOpenMemory est la version simplifiée pour une base en mémoire vive (donc détruite à la fermeture de la connexion) - _sqliteOpenTemp est la version simplifiée pour une base temporaire (fichier temporaire du système hôte qui sera détruit à la fermeture de la connexion) La fonction _sqliteOpenFileEx permet de paramétrer la connexion. Voir la doc de l'API pour plus de détails. Pour fermer la connexion (dbClose), il n'y a qu'une seule fonction : _sqliteClose Cette fonction ne ferme pas forcéme t la connexion. En effet, si des opérations sont en cours par exemple, la fermeture sera annulée. L'envoi de requête se fait via dbExec qui utilise la fonction _sqliteExec. Cette dernière attend en arguments l'objet connexion, la chaîne sql (la requête) suivi d'un troisième argument qui devrait rester à nil. La fonction dbReflex définit la callback à exécuter lors des retours SQL (s'il y a lieu, typiquement avec une commande SELECT par exemple). La callback doit être du type : fun [ObjSqlite u0 S S] I Pour ajouter des entrées, la fonction dbAdd attend une liste de valeurs. Remarquons qu'ici nous exécutons une requête vers la base à chaque élément de la liste ce qui n'est pas très optimisé. Pour créer initialement la table : dbCreateTable. Rien de particulier si ce n'est que ce n'est pas optimisé au niveau des performances. Encore une fois, ce n'est pasle but de cet exemple. dbAdd, dbDel et dbRun montrent différentes utilisations de _sqliteExec. */ // Création de la connexion fun dbOpen()= // set db = _sqliteOpenFile _channel _checkpack strcat PATH BASENAME; set db = _sqliteOpenFileEx _channel _checkpack strcat PATH BASENAME SQLITE_READWRITE nil nil; // set db = _sqliteOpenMemory _channel; // set db = _sqliteOpenTemp _channel; // Nous testons le retour de la connexion if (db == nil) then // échec ( _DLGMessageBox _channel nil "Erreur !" strcatn "Impossible de se connecter à la base de données : " :: PATH :: BASENAME :: nil 0; write _fooS "Connexion à la base de donnees impossible"; 0 ) else // succés ( write _fooS "Connexion à la base de donnees reussie !"; 0 );; // Déconnexion de la base fun dbClose()= let _sqliteClose db -> res in // Nous testons la validité de fin de connexion if (res != 0) then // échec (probablement une (des) opération(s) en cours ( _DLGMessageBox _channel nil "Warning !" strcat "La connexion n'a pu être fermée. Code retournée : " itoa res 0; write _fooS strcat "Connexion non fermee. Code retourne : " itoa res; res ) else // succès ( write _fooS "Connexion à la base fermee !"; 0 );; fun dbCreate()= // Création du fichier DB si celui-ci est inexistant let _checkpack strcat PATH BASENAME -> p in if (p == nil) then _createpack "" _getmodifypack strcat PATH BASENAME else 0;; fun dbExec(sql)= _sqliteExec db sql nil;; fun dbReflex(cbfun, param)= _sqliteCallbackExec db cbfun param;; fun dbCreateTable()= // Nous entrons la requête SQL de création de la table de notre base let "CREATE TABLE listing (name char(64), nickname char(64), email char (64));" -> sql in // Nous l'exécutons // Note : nous aurions pu aussi tester la présence de la table ... let dbExec sql -> res in if (res != 0) then // échec (il se peut que la table existe déjà) ( write _fooS strcat "La creation de la table a echouee ! Code retournee : " itoa res; res ) else // succès (la table n'existait pas auparavant) ( write _fooS "La table a ete correctement creee !"; 0 );; fun dbCBgetDatas(D, p, column, value)= let p -> [win listname listnickname listemail form] in ( if (!strcmp column "name") then _ADDlist listname 0 value else if (!strcmp column "nickname") then _ADDlist listnickname 0 value else if (!strcmp column "email") then _ADDlist listemail 0 value else nil; 0 );; fun dbGetDatas(p)= // Nous définissons la callback appelée lors de l'envoi de requête SQL dbReflex @dbCBgetDatas p; let "SELECT name, nickname, email FROM listing" -> sql in let dbExec sql -> res in write _fooS strcatn "Resultat de la requete " :: sql :: " : " :: (itoa res) :: nil; 0;; fun dbAdd(l)= if l == nil then ( write _fooS "Donnees ajoutees avec succes !"; 0 ) else let hd l -> [name nick mail] in let strcatn "INSERT INTO listing (name, nickname, email) VALUES ('" :: name :: "', '" :: nick :: "', '" :: mail :: "')" :: nil -> sql in let dbExec sql -> res in if (res != 0) then ( write _fooS strcatn "La requete " :: sql :: " a provoque une erreur " :: (itoa res) :: nil; 1 ) else dbAdd tl l;; fun dbDel(l, out)= if (l == nil) then let dbExec out -> res in if (res != 0) then ( write _fooS strcatn "La requete " :: out :: " a provoque une erreur " :: (itoa res) :: nil; 1 ) else ( write _fooS "Donnees supprimees avec succes !"; 0 ) else let hd l -> name in let strcatn "DELETE FROM listing WHERE (name='" :: name :: "');" :: nil -> sql in dbDel tl l strcat out sql;; fun dbRun(sql)= let dbExec sql -> res in if (res != 0) then ( write _fooS strcatn "La requete " :: sql :: " a provoque une erreur " :: (itoa res) :: nil; 1 ) else ( write _fooS strcatn "La requete " :: sql :: " a ete executee avec succes !" :: nil; 0 );; /* Fonctions relatives à l'interface graphique - réflexes des boutons, - création et destruction - gui secondaires */ fun guiRstLists(p)= let p -> [_ listname listnickname listemail _] in ( _RSTlist listname; _RSTlist listnickname; _RSTlist listemail; 0 );; fun guiAddData(obj, u)= let u -> [p [winadd ename enick email]] in let _GETtext ename -> name in let _GETtext enick -> nick in let _GETtext email -> mail in ( _DSwindow winadd; dbAdd [name nick mail] :: nil; guiRstLists p; dbGetDatas p; 0 );; fun guiCreateAdd(p)= let p -> [win listname listnickname listemail form] in let [500 110] -> [w h] in let _CRwindow _channel win 5 5 w h WN_NORMAL "Add any data" -> winadd in let _SETtextFocus _CReditLine _channel winadd 100 5 w-105 20 ET_DOWN|ET_AHSCROLL|ET_TABFOCUS "" -> ename in let _CReditLine _channel winadd 100 30 w-105 20 ET_DOWN|ET_AHSCROLL|ET_TABFOCUS "" -> enick in let _CReditLine _channel winadd 100 55 w-105 20 ET_DOWN|ET_AHSCROLL|ET_TABFOCUS "" -> email in let [winadd ename enick email] -> padd in ( _CRtext _channel winadd 5 5 90 20 ET_BORDER "Name :"; _CRtext _channel winadd 5 30 90 20 ET_BORDER "Nick :"; _CRtext _channel winadd 5 55 90 20 ET_BORDER "Email :"; _CBbutton _CRbutton _channel winadd 5 80 w-10 20 PB_TABFOCUS "Add !" @guiAddData [p padd]; 0 );; // callback bouton Add fun guiAdd(obj, p)= guiCreateAdd p; 0;; // callback bouton Del fun guiDel(obj, p)= let p -> [win listname listnickname listemail form] in let _GETlistMSel listname -> lname in let sizelist lname -> n in let nil -> out in ( while n >= 0 do let nth_list lname n -> [_ name] in ( set out = name :: out; set n = n-1 ); dbDel out ""; guiRstLists p; dbGetDatas p; 0 );; fun guiRun(obj, u)= let u -> [sql p] in ( dbRun _GETtext sql; guiRstLists p; dbGetDatas p; 0 );; // callback bouton Edit fun guiEdit(obj, p)= 0;; // callback bouton Search fun guiSearch(obj, p)= 0;; // callback destruction de la fenêtre fun endwin(win, u)= end;; // Création de l'interface graphique fun guiCreate()= let _GETscreenSize -> [ws hs] in let [640 480] -> [w h] in let 90 -> hlog in let _CRwindow _channel nil (ws-w)/2 (hs-h)/2 w h+hlog WN_MENU|WN_MINBOX "Test 1 SQLITE" -> win in let _CRlist _channel win 5 30 (w/3)-20 h-85 LB_DOWN|LB_BORDER|LB_MULTIPLE -> listname in let _CRlist _channel win 5+(w/3)+5 30 (w/3)-20 h-85 LB_DOWN|LB_BORDER|LB_NOSELECTION -> listnickname in let _CRlist _channel win 5+(2*(w/3))+(2*5) 30 (w/3)-20 h-85 LB_DOWN|LB_BORDER|LB_NOSELECTION -> listemail in let _CReditLine _channel win (4*(w/5))-(3*5) h-50 (w/5)+10 20 ET_DOWN|ET_AHSCROLL "" -> form in let _CReditLine _channel win 5 h-25 w-(w/5)-15 20 ET_DOWN|ET_AHSCROLL "" -> sql in let [win listname listnickname listemail form] -> p in ( _CRtext _channel win 5 5 (w/3)-20 20 ET_ALIGN_CENTER "Name :"; _CRtext _channel win 5+(w/3)+5 5 (w/3)-20 20 ET_ALIGN_CENTER "Nickame :"; _CRtext _channel win 5+(2*(w/3))+(2*5) 5 (w/3)-20 20 ET_ALIGN_CENTER "Email :"; set tlog = _CRtext _channel win 5 h+5 w-10 hlog-10 ET_HSCROLL|ET_VSCROLL|ET_DOWN "résultat :"; _CBwinDestroy win @endwin 0; _CBbutton _CRbutton _channel win 5 h-50 (w/5)-10 20 0 "Add" @guiAdd p; _CBbutton _CRbutton _channel win (w/5) h-50 (w/5)-10 20 0 "Del" @guiDel p; _ENbutton _CBbutton _CRbutton _channel win (2*(w/5))-5 h-50 (w/5)-10 20 0 "Edit" @guiEdit p 0; _ENbutton _CBbutton _CRbutton _channel win (3*(w/5))-10 h-50 (w/5)-10 20 0 "Search ->" @guiSearch p 0; _CBbutton _CRbutton _channel win w-(w/5)-5 h-25 w/5 20 0 "<- Run SQL" @guiRun [sql p]; p );; /* Fonction d'appel du programme */ fun main_sqlite()= // Création de l'interface graphique // guiCreate; // Création de la abse, s'il y a lieu dbCreate; // Connexion à la base dbOpen; // Création de la table s'il y a lieu dbCreateTable; // Remplissage de l'interface graphique avec le contenu de la base (si non vierge) dbGetDatas guiCreate; 0;;