Page 1 sur 2

SQLite Explorer

Publié : dim. 11/oct./2015 20:48
par falsam
SQLite Explorer est un code PureBasic léger, pour explorer une base de données SQLite en exécutant des requêtes SQL.

Image

1 - Sélectionner une base de données.
2 - Vous allez pouvoir saisir ou modifier une requête SQL.
3 - Affichages des tables ainsi que la structure de chacune de ces tables.
4 - Affiche le résultat d'une requête SQL.
5 - Rapport d’exécution d'une requête SQL.

Touche(s) de raccourci.
F5 - Exécution d'une requête.

Les touches ou combinaisons de touches ci-dessous permettent d'afficher les différentes requêtes SQL que vous avez effectuées.
Ctrl + Home - Afficher la première requête.
Ctrl + End - Afficher la dernière requête.
PageUp - Requête précédente.
PageDown - Requête suivante.

■ Commentaires.
Les requêtes que vous avez effectuées sont sauvegardées quand vous quittez l'application ou quand vous sélectionnez une autre bases de données.
Le nom du fichier de sauvegarde se compose du nom de la base de données suivi de l'extension .JSON.
Exemple: Si vous explorez la base de données codepostaux.db3 le fichier de sauvegarde contenant les requêtes SQL se nommera codepostaux.db3.json

■ Code.

Code : Tout sélectionner

; SQLite Explorer (falsam)
;
; PB PB 4.40
;
; Create  : 2015 Octobre 11
; Update  : 2016 Février 14

EnableExplicit

Enumeration Font
  #FontGlobal
EndEnumeration

Enumeration Window
  #MainForm                      ;Fenetre principale
  #RecordForm                    ;Insertion ou modification d'un enregistrement
EndEnumeration

Enumeration Gadget
  #DatabaseSelect                ;Selecteur de base de données
  #UseUnescapeString             
  #DataBase                      ;Base de données en cours de visualisation
  #ReqSql                        ;Saisie des requêtes SQL
  #ListTables                    ;Liste des tables 
  #ListRows                      ;Contenue d'une table
  #Report
  #Help
  #Splitter1
  #Splitter2
  #Splitter3
  
  #RFContainer
  #RFSubmit                      ;Ajouter ou modifier un enregistrement (Bouton)
EndEnumeration

Enumeration GadgetViewRecord #PB_Compiler_EnumerationValue 
  #Field
EndEnumeration

Enumeration ShortCut
  #F5                            ;Execution d'une requête
  #Home                          ;Premiere requete SQL (Ctrl + Home)
  #End                           ;Derniere requete SQL (Ctrl + End)
  #PageUp                        ;Requete SQL précédente
  #PageDown                      ;Requete SQL suivante
  #Enter                         ;Modifier un rangée de données 
EndEnumeration

Structure NewReqSql
  ReqSql.s
EndStructure

Global NewList ReqSql.NewReqSql()  ;Mémorisation des requétes SQL

;Plan de l'application
Declare MainFormShow()          ;Fenetre principale de l'application
Declare ShowReport(Buffer.s)    ;Affichage du rapport d'éxécution

Declare WorkReqSqlLoad()        ;Chargement des requêtes SQL associées à la base de données sélectionnée
Declare WorkReqSqlSave()        ;Sauvegarde des requêtes SQL associées à la base de données sélectionnée

Declare OnDataBaseSelect()      ;Une base de données est sélectionnée
Declare DataBaseListTable()     ;Affichage des tables de la base de données 
Declare OnTableSelect()         ;Une table est sélectionnée 

Declare OnRecordSelect()        ;

Declare OnReqSQLExe()           ;Une requête SQL est éxécutée
Declare OnReqSQLSelect()        ;Défilement & Selection d'une requête SQL
Declare OnReqSQLError(ReqSql.s) ;Erreur lors de l'éxéctution d'une requête SQL

Declare OnResizeWindow()        ;Resize Window
Declare OnCloseWindow()         ;Fin de l'application

UseSQLiteDatabase()

MainFormShow()

;Fenetre principale de l'application
Procedure MainFormShow()
  LoadFont(#FontGlobal, "", 10)
  SetGadgetFont(#PB_Default, FontID(#FontGlobal))
  
  If OpenWindow(#MainForm, 0, 0, 1020, 760, "SQlite Explorer", #PB_Window_ScreenCentered|#PB_Window_SizeGadget|#PB_Window_MaximizeGadget)      
    
    ;Selecteur de base de données 
    ButtonGadget(#DatabaseSelect, 410, 10, 22, 22, "?")
    GadgetToolTip(#DatabaseSelect, "Selectionner une base de données")
    
    CheckBoxGadget(#UseUnescapeString, 460, 10, 150, 22, "Use UnescapeString")    
    
    ;Base de donnée en cours de consultation
    TextGadget(#PB_Any, 10, 15, 100, 20, "Database")
    StringGadget(#Database, 105, 10, 300, 22, "?", #PB_String_ReadOnly)
    
    ;Affichage de l'editeur de requete SQL (haut de la fenetre)
    EditorGadget(#ReqSQL, 0, 0, 0, 0)
    
    ;Affichage des tables et de la structure de chaque table (Gauche de la fenetre)
    TreeGadget(#ListTables, 0, 0, 0, 0)
    
    ;Affichage du contenu d'une table sélectionneé dans #ListTables (Droite de la fenetre)
    ListIconGadget(#ListRows, 0, 0, 0, 0, "?", 1000, #PB_ListIcon_FullRowSelect|#PB_ListIcon_GridLines|#PB_ListIcon_HeaderDragDrop|#PB_ListIcon_AlwaysShowSelection)
    
    ;Debug (Bas de la fenetre)
    ListViewGadget(#Report, 0, 0, 0, 0)
    
    ;Zone d'aide
    TextGadget(#Help, 10, 735, 1000, 22, "[F5] Run SQL  -  [Ctrl + Home], [Ctrl + End], [PageUp], [PageDown] Select query sql.")
    
    ;Mise en place ds splitters
    SplitterGadget(#Splitter1, 10, 40, 1000, 690, #ListTables, #ListRows, #PB_Splitter_Vertical|#PB_Splitter_FirstFixed)
    SetGadgetState(#Splitter1, 200) ;Positionne le splitter 1 à 200 px du bord gauche
    
    SplitterGadget(#Splitter2, 10, 40, 1000, 690, #ReqSql, #Splitter1, #PB_Splitter_FirstFixed)
    SetGadgetState(#Splitter2, 100) ;Positionne le splitter 2 à 100 px du bord haut
    
    SplitterGadget(#Splitter3, 10, 40, 1000, 690, #Splitter2, #Report, #PB_Splitter_SecondFixed)
    SetGadgetState(#Splitter3, 600) ;Positionne le splitter 3 à 600 px du bord haut
    
    ;Shortcuts
    AddKeyboardShortcut(#MainForm, #PB_Shortcut_F5, #F5)                            ;Exécution d'une requéte SQL
    AddKeyboardShortcut(#MainForm, #PB_Shortcut_Control|#PB_Shortcut_Home, #Home)   ;Premiere requete SQL
    AddKeyboardShortcut(#MainForm, #PB_Shortcut_Control|#PB_Shortcut_End, #End)     ;Derniere requete SQL
    AddKeyboardShortcut(#MainForm, #PB_Shortcut_PageDown, #PageDown)                ;Requete SQL précédente
    AddKeyboardShortcut(#MainForm, #PB_Shortcut_PageUp, #PageUp)                    ;Requete SQL suivante
    AddKeyboardShortcut(#MainForm, #PB_Shortcut_Return, #Enter)                     ;Un enregistrement est sélectionné
    
    ;Evenements
    BindGadgetEvent(#DatabaseSelect, @OnDataBaseSelect(), #PB_EventType_LeftClick)  ;Une base de donnés est sélectionnée
    BindGadgetEvent(#ListTables, @OnTableSelect(), #PB_EventType_LeftClick)         ;Une table est sélectionnée
    BindEvent(#PB_Event_Menu, @OnReqSQLExe(), #MainForm, #F5)                       ;Une requête SQL est éxécutée
    BindEvent(#PB_Event_Menu, @OnReqSQLSelect(), #MainForm, #Home)                  ;Sélection de la premiere requete SQL
    BindEvent(#PB_Event_Menu, @OnReqSQLSelect(), #MainForm, #End)                   ;Sélection de la derniere requete SQL
    BindEvent(#PB_Event_Menu, @OnReqSQLSelect(), #MainForm, #PageUp)                ;Sélection de la requete SQL précédente
    BindEvent(#PB_Event_Menu, @OnReqSQLSelect(), #MainForm, #PageDown)              ;Sélection de la requete SQL suivante
    
    BindEvent(#PB_Event_Menu, @OnRecordSelect(), #MainForm, #Enter)                 ;Un enregistrement est sélectionné 
    
    BindEvent(#PB_Event_SizeWindow, @OnResizeWindow())                              ;Redimensionne la fenêtre
    BindEvent(#PB_Event_CloseWindow, @OnCloseWindow())                              ;Fermeture de l'application   
    
    Repeat : Until WaitWindowEvent(10) = #PB_Event_CloseWindow
  EndIf
EndProcedure


Procedure RecordShow()
  Protected Col, y, Spaced = 10
  Protected Index = GetGadgetState(#ListRows)
  
  OpenWindow(#RecordForm, 0, 0, 500, 400, "", #PB_Window_ScreenCentered|#PB_Window_SizeGadget|#PB_Window_MaximizeGadget)
  SetWindowColor(#RecordForm, RGB(255, 255, 255))
  
  ;Container
  ScrollAreaGadget(#RFContainer, 0, 0, 500, 350, 450, 2000, 10, #PB_ScrollArea_Flat)
  
  While GetGadgetItemAttribute(#ListRows, 0,  #PB_ListIcon_ColumnWidth, Col)  
    TextGadget(#PB_Any, 10, (y * 50) + Spaced , 150, 22, GetGadgetItemText(#ListRows, -1, Col))
    EditorGadget(#Field + Col, 160, (y * 50) + Spaced, 290, 42)
    SetGadgetItemText(#Field + Col, 0, GetGadgetItemText(#ListRows, Index, Col))
    Col+1 : y + 1
  Wend
  
  ;Fermeture du container
  CloseGadgetList()
  
  ;Le nombre de gadgets de cette fenêtre est mémorisé en data du gadget #Field
  SetGadgetData(#Field, Col)
  
  ;La rangée de la table de visualisation est mémorisée en data du gadget container
  SetGadgetData(#RFContainer, Index)
  
  ;Le numéro d'enregistrement de la table est mémorisée en date de la fenetre
  SetWindowData(#RecordForm, Col)
  
  WindowBounds(#RecordForm, 400, 200, #PB_Ignore, #PB_Ignore)
  StickyWindow(#RecordForm, #True)
  
EndProcedure


;Affichage du rapport d'éxécution
Procedure ShowReport(Buffer.s)
  Protected TimeStamp.s = "[" + FormatDate("%hh:%ii:%ss", Date()) + "] "
  
  AddGadgetItem(#Report, -1, TimeStamp + Buffer)
  SetGadgetState(#Report, CountGadgetItems(#Report) - 1)
EndProcedure

;Recherche de requêtes SQL associées à la base de données sélectionnée
Procedure WorkReqSqlLoad()
  Protected FileName.s = GetFilePart(GetGadgetText(#DataBase))
  Protected PathName.s = GetPathPart(GetGadgetText(#DataBase))
  Protected JSONName.s = PathName + Filename + ".json"
  Protected JSON 
  
  If ReadFile(0, JSONName)
    CloseFile(0)
    
    JSON = LoadJSON(#PB_Any, JSONName, #PB_JSON_NoCase)
    ExtractJSONList(JSONValue(JSON), ReqSql())
    FreeJSON(JSON)
    
    ShowReport("Vous avez " + Str(ListSize(ReqSql())) + " requétes mémorisées pour cette base de données.")
  EndIf
EndProcedure

;Sauvegardes des requetes SQL associées à la base de données sélectionnée
Procedure WorkReqSqlSave()
  Protected FileName.s = GetFilePart(GetGadgetText(#DataBase))
  Protected PathName.s = GetPathPart(GetGadgetText(#DataBase))
  Protected JSONName.s = PathName + Filename + ".json"
  
  Protected JSON = CreateJSON(#PB_Any)
  ;Si une base de données est déja ouverte ?
  ;Fermeture de la base de données  
  If IsDatabase(#Database)
    CloseDatabase(#DataBase)
    
    If ListSize(ReqSql()) > 0
      InsertJSONList(JSONValue(JSON), ReqSql())
      SaveJSON(JSON, JSONName, #PB_JSON_PrettyPrint)
      FreeJSON(JSON)
    EndIf
  EndIf
EndProcedure

;Une base de données est sélectionnée
Procedure OnDataBaseSelect()  
  Protected Database.s = OpenFileRequester("Selectionner une base de données SQLite", "*.sqlite", "", 0)
  
  ;Une base de données est sélectionnée
  If Database    
    WorkReqSqlSave()
    
    ;Reset des différents gadgets et table
    ClearGadgetItems(#ListTables)
    ClearGadgetItems(#ListRows)
    ClearGadgetItems(#Report)
    SetGadgetText(#ReqSql, "")
    SetGadgetText(#Database, Database)
    ClearList(ReqSql())
    
    ;Ouverture de la base de données sélectionnée
    ;Il s'agit d'une base de données SQLite
    ;Username & Password sont inutiles
    If OpenDatabase(#DataBase, Database, "","") 

      ;Affichage des tables
      DataBaseListTable()
    Else
      Debug "passe"
    EndIf
    
  EndIf
EndProcedure

;Affichage de la liste des tables et de la structure de chaque table
Procedure DataBaseListTable()
  Protected ReqSql.s, Table.s, Buffer.s , Dim Tables.s(0), n
  
  ;Extraction de la liste des tables
  ReqSQL="Select * From sqlite_master order by type Desc, name Asc"
  
  ShowReport(ReqSql)
  If DatabaseQuery(#Database, ReqSQL)  
    While NextDatabaseRow(#Database)
      If GetDatabaseString(#Database,0)="table"
        Tables(n) = GetDatabaseString(#Database,1)
        n+1 : ReDim Tables(n)
      EndIf  
    Wend
    
    ;Affichage des tables
    For n = 0 To ArraySize(Tables()) - 1
      AddGadgetItem(#ListTables, -1, Tables(n))
      
      ;Affichage de la Structure de chacune des tables
      ReqSQL="PRAGMA table_info(" + Chr(34) + Tables(n) + Chr(34) + ")"
      ShowReport(ReqSql)
      If DatabaseQuery(#Database, ReqSQL)
        While NextDatabaseRow(#Database)
          Buffer = GetDatabaseString(#Database, 1) + " " 
          Buffer + GetDatabaseString(#Database, 2) + " " 
          If GetDatabaseString(#Database, 5) = "1"
            Buffer + "PRIMARY KEY"
          EndIf
          
          AddGadgetItem(#ListTables, -1, Buffer, 0, 1)
        Wend
      Else
        OnReqSQLError(ReqSql)
      EndIf  
    Next
    
    WorkReqSqlLoad()            
  Else 
    OnReqSQLError(ReqSql)
  EndIf
EndProcedure

;Une table est sélectionnée
Procedure OnTableSelect()
  Protected Table.s, ReqSql.s
  
  If GetGadgetState(#ListTables) <> -1
    If GetGadgetItemAttribute(#ListTables, GetGadgetState(#ListTables), #PB_Tree_SubLevel) = 0 
      
      ;Mémorisation de la table à visualiser 
      Table = GetGadgetItemText(#ListTables, GetGadgetState(#ListTables))
      
      ;Création de la requéte
      ReqSql = "select * from " + Table + " limit 0, 100"
      SetGadgetText(#ReqSql, ReqSql)
    EndIf
  EndIf 
EndProcedure


;Si éxécution d'une requete SQL
;Affiche le contenue d'une table
Procedure OnReqSQLExe()
  Protected ReqSql.s, Buffer.s, Col.s, i
  
  ;Requete de sélection des enregistrements
  ReqSql = GetGadgetText(#ReqSql)
  
  ;Une base de données doit etre ouverte
  ;Existe t'il une requete ?
  If IsDatabase(#DataBase) And ReqSql <> ""    
    ClearGadgetItems(#ListRows)
    
    ;Suppression des colonnes existantes 
    While GetGadgetItemText(#ListRows, -1, 0)
      RemoveGadgetColumn(#ListRows, 0)
    Wend
    
    ;Exécution de la requête SQL
    ShowReport(ReqSql)
    If DatabaseQuery(#Database, ReqSQL)
      
      ;AJout des colonnes
      For i = 0 To DatabaseColumns(#Database) - 1
        Col = DatabaseColumnName(#DataBase, i)
        AddGadgetColumn(#ListRows, i, Col, 100)
      Next     
      
      ;Affichage du contenu de la table
      While NextDatabaseRow(#DataBase)
        For i = 0 To DatabaseColumns(#DataBase) - 1
          Buffer + GetDatabaseString(#DataBase, i) + Chr(10)
        Next
        If GetGadgetState(#UseUnescapeString) = #True
          AddGadgetItem(#ListRows, -1, UnescapeString(Buffer, #PB_String_EscapeXML))
        Else
          AddGadgetItem(#ListRows, -1, Buffer)
        EndIf
        Buffer = ""
      Wend
      
      ;Sauvegarde de la requéte
      If ListSize(ReqSql()) = 0 Or ReqSql <> ReqSql()\ReqSql
        AddElement(ReqSql())
        ReqSql()\ReqSql = ReqSql
      EndIf
    Else
      OnReqSQLError(ReqSql)
    EndIf
  EndIf
EndProcedure

;Défilement & Selection d'une requete
Procedure OnReqSQLSelect()  
  If ListSize(ReqSql()) <> 0    
    Select EventMenu()
      Case #Home ;Premiere requête
        FirstElement(ReqSql())
        
      Case #End ;Derniere requête
        LastElement(ReqSql())
        
      Case #PageUp ;Requête précédente
        PreviousElement(ReqSql())
        
      Case #PageDown ;Requête suivante
        NextElement(ReqSql())
        
    EndSelect
    
    SetGadgetText(#ReqSql, ReqSql()\ReqSql)
  Else
    ShowReport("Aucune requête mémorisée !")
  EndIf
  
EndProcedure

;Si Erreur durant l'éxécution d'une requête SQL
Procedure OnReqSQLError(ReqSql.s)
  ShowReport(DatabaseError())
EndProcedure

;-
;- U.T - Record
;Modification d'une rangée de données 
Procedure OnRecordSelect()
  Protected Index = GetGadgetState(#ListRows)
  
  If Index <> -1
    RecordShow()
  EndIf
EndProcedure


;Redimensionne la fenetre active
Procedure OnResizeWindow()
  Protected Window = EventWindow()
  Protected Width = WindowWidth(Window)
  Protected Height = WindowHeight(Window) 
  Protected Gadget
  
  Select Window
    Case #MainForm
      ResizeGadget(#Splitter3, #PB_Ignore, #PB_Ignore, Width-20, Height-70)
      ResizeGadget(#Help, #PB_Ignore, Height - 25, Width-20, #PB_Ignore)
      
    Case #RecordForm
      ResizeGadget(#RFContainer, #PB_Ignore, #PB_Ignore, Width, Height - 50)
      SetGadgetAttribute(#RFContainer, #PB_ScrollArea_InnerWidth, Width - 50)
      For Gadget = #Field To #Field + GetGadgetData(#Field) - 1
          ResizeGadget(Gadget, #PB_Ignore, #PB_Ignore, Width - GadgetX(Gadget) - 50, #PB_Ignore)
      Next
    
  EndSelect
EndProcedure

;Fermeture de l'application
Procedure OnCloseWindow()   
  Protected Window = EventWindow()
  
  Select Window
    Case #RecordForm
      CloseWindow(#RecordForm)
      
    Case #MainForm
      WorkReqSqlSave()
      End
  EndSelect 
EndProcedure
Pour vous entraîner.
Une base de données contenant des codes postaux et des départements que vous pouvez télécharger.
codepostaux.db3

■ GitHub
https://github.com/falsam/SQLite-Explorer

Re: SQLite Explorer

Publié : lun. 12/oct./2015 7:25
par Micoute
Merci falsam pour le partage.

Re: SQLite Explorer

Publié : lun. 12/oct./2015 18:09
par kwandjeen
Merci pour le partage :D

Re: SQLite Explorer

Publié : mar. 13/oct./2015 7:24
par blendman
Salut

Merci.

J'ai deux questions :
- est-ce que ça fonctionne avec des BDD Mysql ?
- est-ce qu'on peut ensuite modifier le code pour gérer des bdd qui ne sont pas sur notre poste, mais sur notre serveur par exemple ?

Re: SQLite Explorer

Publié : mar. 13/oct./2015 7:57
par microdevweb
Blendman,

A mon avis pour que cela fonctionne avec Mysql tu doit passé par ODBC.

Re: SQLite Explorer

Publié : mar. 13/oct./2015 8:18
par falsam
blendman a écrit :est-ce que ça fonctionne avec des BDD Mysql ?
Actuellement le code ne permet de gérer que des bases de données SQLite.

Il peut être adapté facilement pour explorer des bases de données PostgreSQL en natif avec PureBasic.

Par contre PureBasic ne gérant pas nativement MySQL, deux solutions se présentent :
- Passez par ODBC comme le suggère microdevweb aprés avoir installer un driver ODBC pour MySQL.
- Se servir d'un wrapper MYSQL en PureBasic.
blendman a écrit :est-ce qu'on peut ensuite modifier le code pour gérer des bdd qui ne sont pas sur notre poste, mais sur notre serveur par exemple ?
Oui parce que c'est une solution que j'ai déja exploité dans un autre code. Mais il faut savoir que la plupart des hébergeurs mutualisés empêchent ce type de fonctionnement. Ce type de connexion MySQL ne peut se faire que sur des serveurs virtuels ou dédiés.

Re: SQLite Explorer

Publié : mar. 13/oct./2015 8:42
par falsam
Nouvelle version de SQLite Explorer.

Ajout de la sauvegarde des requêtes.
Les requêtes que vous avez effectuées sont sauvegardées quand vous quittez l'application ou quand vous sélectionnez une autre base de données SQLite.

Le nom du fichier de sauvegarde contenant vos requêtes se compose du nom de la base de données suivi de l'extension .JSON.
Exemple: Si vous explorez la base de données codepostaux.db3 le fichier de sauvegarde se nommera codepostaux.db3.json

Ajout de quelques touches de raccourcis.
Les touches ou combinaisons de touches ci-dessous permettent d'afficher les différentes requêtes SQL que vous avez effectuées.
Ctrl + Home - Afficher la première requête.
Ctrl + End - Afficher la dernière requête.
PageUp - Requête précédente.
PageDown - Requête suivante.

Le premier message de ce topic est mis à jour avec le nouveau code.

Re: SQLite Explorer

Publié : mar. 13/oct./2015 9:50
par Kwai chang caine
J'ose à peine le dire de peur de me faire engueuler comme la derniere fois ..mais je me lance quand même...
Merci pour le partage :oops:

Re: SQLite Explorer

Publié : mer. 14/oct./2015 13:35
par falsam
Kwai chang caine a écrit :J'ose à peine le dire de peur de me faire engueuler comme la derniere fois
Hein? Qu'ouie je, qu'entends je, qu'acoustique de mes deux oreilles (Du grand n’importe quoi dans ces exclamation)? Je t'ai engueuler ?

Re: SQLite Explorer

Publié : mer. 14/oct./2015 14:21
par GG
Merci falsam, fonctionne parfaitement.

Re: SQLite Explorer

Publié : mer. 14/oct./2015 14:36
par Kwai chang caine
Hein? Qu'ouie je, qu'entends je, qu'acoustique de mes deux oreilles (Du grand n’importe quoi dans ces exclamation)? Je t'ai engueuler ?
:cry:
http://www.purebasic.fr/french/viewtopi ... 85#p176985

Re: SQLite Explorer

Publié : ven. 11/déc./2015 9:23
par microdevweb
Merci Falsam pour outils très pratique qui me permet de tester mes requètes :wink:

Re: SQLite Explorer

Publié : ven. 11/déc./2015 10:03
par Patrick88
Encore un code parfait, avec des explications claires, exempt de bug, de la part de Falsam,
pfff, y m'énerve, mais y m'énerve.... :roll: :wink:

ah, si, y'a pas d'objet, pourtant vu ce qui nous a bassiné avec, mais bon, nul n'est parfait....

:mrgreen:

comment j'ai pus louper ce code ? ça date pas d'hier en plus !

remarque: essayé avec les bases de l'éditeur de microdevweb (speeddev) en 5.31 et 5.40 LTS

Pat

Re: SQLite Explorer

Publié : mar. 15/déc./2015 12:51
par falsam
Patrick88 a écrit :ah, si, y'a pas d'objet, pourtant vu ce qui nous a bassiné avec, mais bon, nul n'est parfait....
Merci Pat pour cette pointe d'ironie ^^

Vous pouvez télécharger le source depuis GitHub https://github.com/falsam/SQLite-Explorer

Re: SQLite Explorer

Publié : mar. 15/déc./2015 14:15
par Kwai chang caine
Merci de ta générosité 8)