Droopy Lib - Little DataBase - Module Version

Share your advanced PureBasic knowledge/code with the community.
User avatar
StarBootics
Addict
Addict
Posts: 1006
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Droopy Lib - Little DataBase - Module Version

Post by StarBootics »

Hello everyone,

While I'm experimenting about how the Module work I have updated the Droopy's Little DataBase Lib. Originally coded for PureBasic V3.93
I hope this example can help others about using module.

Edit #1: After a deeper inspection of the code I make few more changes.
Edit #2 : I'm sorry for those who have save this code on their computer. You will have to do it again or just replace the LDB_CloseDataBase() precedure by this one :

Code: Select all

  Procedure LDB_CloseDataBase()
    
    ClearList(LdbBdd())
    ClearList(LdbField())
    
    LdbDatabaseFile.s = ""
    LdbFieldCount = 0
    LdbSearchStop = 0 
    LdbSearchReturn = 0
    LdbSearchField = 0 
    LdbSearchPointer = 0
    LdbSearchOption = 0 
    LdbSearchString.s = ""
    LdbFlag.b = 0
    
    If IsRegularExpression(Reg_Ex_IsNumeric)
      FreeRegularExpression(Reg_Ex_IsNumeric)
      Reg_Ex_IsNumeric = #Null
    EndIf
    
  EndProcedure
Best regards
StarBootics

Code: Select all

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Project name : Little DataBase - Module Version
; File Name : LittleDataBase - Module.pb
; File version: 1.0.0
; Programming : OK
; Programmed by : StarBootics
; Date : 12-02-2015
; Last Update : 12-02-2015
; PureBasic code : V5.31
; Platform : Windows, Linux, MacOS X
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Programming notes :
;
; This my first try using module in PureBasic. This code came from the Droopy Lib, so I 
; deserve credit only for conversion to Module, the LDB_FormatFields() command addition, 
; the verification if the field is really numeric in the LDB_SortNum() command and a more 
; complete clean up for the LDB_CloseDatabase() command. 
;
; Only tested with PureBasic 5.31 x64 under Ubuntu Gnome 14.10 x64. Should work fine on 
; other OS as is.
;
; I DESERVE NO CREDIT FOR 98% OF THIS CODE. The original author deserve the honor !
;
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Original Comment from the Droopy Lib :
;
; Ldb (Start)                                                   
; PureBasic 3.93
; Library for managing little local Database ( Little DataBase = LDB )
; Droopy 08/03/05-19/03/05 and 13/06/05-
; Version 1.1 ( Integration in Droopy Lib)
; Remove LdbInit 
; Translation French to English
;
;  RES RES RES RES RES RES RES RES RES RES RES RES RES RES RES RES 
;/ #CaseInsensitive=1     ; Need to be in .res
;/ #EveryWhere=2          ; Need to be in .res
;
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

DeclareModule LittleDataBase
  
  #LDB_CASE_INSENSITIVE = 1
  #LDB_EVERYWHERE = 2
  
  Declare LDB_GetPointer()
  Declare LDB_SetPointer(RecordID)
  Declare LDB_CountField()
  Declare LDB_OpenDataBase(Database.s)
  Declare LDB_CreateDataBase(Database.s, Fields.s)
  Declare LDB_CountRecord()
  Declare.s LDB_Read(FieldID)
  Declare LDB_Write(FieldID, Text.s)
  Declare LDB_AddRecord()
  Declare LDB_DeleteRecord()
  Declare.b LDB_SaveDataBase()
  Declare LDB_CloseDataBase()
  Declare LDB_SetFieldName(Nb, FieldName.s)
  Declare.s LDB_GetFieldName(Nb)
  Declare LDB_PreviousRecord()
  Declare LDB_NextRecord()
  Declare LDB_InsertRecord(Nb.l)
  Declare LDB_SearchInit(FieldID, SearchText.s, Option)
  Declare LDB_Search()
  Declare LDB_SortNum(FieldID)
  Declare LDB_SortAlpha(FieldID, Option)
  Declare.s LDB_FormatFields(Separator.s)
  
EndDeclareModule

Module LittleDataBase
  
  ;/ Constants
  #Separator = Chr(1)
  #FieldSeparator = ","
  #CRLFSubstitution = Chr(2)
  
  Global LdbDatabaseFile.s, LdbFieldCount.l, LdbSearchStop.l, LdbSearchReturn.l
  Global LdbSearchField.l, LdbSearchPointer.l, LdbSearchOption.l, LdbSearchString.s
  Global LdbFlag.b, Reg_Ex_IsNumeric.i
  
  Global NewList LdbBdd.s()
  Global NewList LdbField.s()
  
  ;  Initialise the Global Variable / LinkedList ( Internal Function )
  Procedure LDB_Init() 
    
    If LdbFlag=0
      LdbFlag=1
    EndIf
    
  EndProcedure
  
  ;  Get the index of the current record 
  Procedure LDB_GetPointer()
    If ListSize(LdbBdd())
      ProcedureReturn ListIndex(LdbBdd())
    EndIf
  EndProcedure
  
  ;  Set the index of the current record 
  Procedure LDB_SetPointer(RecordID)
    If ListSize(LdbBdd())
      SelectElement(LdbBdd(),RecordID)
    EndIf
  EndProcedure
  
  ;  Count the number of Fields
  Procedure LDB_CountField()
    
    If ListSize(LdbBdd())
      
      tmp = LDB_GetPointer()
      FirstElement(LdbBdd())
      LdbFieldCount = CountString(LdbBdd(),#Separator) + 1
      LDB_SetPointer(tmp)
      
      ProcedureReturn LdbFieldCount
      
    EndIf
    
  EndProcedure
  
  ;  Open an existing Database ( Return 1 if success / 0 if fail ) Thanks to Baldrick
  Procedure LDB_OpenDataBase(Database.s)
    
    LDB_Init()
    
    FileID = ReadFile(#PB_Any, Database)
    
    If IsFile(FileID)
      
      Retour = 1
      LdbDatabaseFile = Database
      
      ClearList(LdbBdd())
      ClearList(LdbField())
      
      While Eof(FileID) = 0
        AddElement(LdbBdd())
        LdbBdd() = ReadString(FileID)
      Wend
      
      CloseFile(FileID)
      LDB_CountField()
      
    EndIf
    
    ProcedureReturn Retour
  EndProcedure
  
  ;  Create a new empty database
  Procedure LDB_CreateDataBase(Database.s, Fields.s)
    
    LDB_Init()
    LdbDatabaseFile = Database
    ClearList(LdbBdd())
    ClearList(LdbField())
    ttmp.s = ""
    maxfields = CountString(Fields, #FieldSeparator) + 1
    
    For n=1 To maxfields
      ttmp + StringField(Fields, n, #FieldSeparator)
      If n <> maxfields
        ttmp + #Separator
      EndIf
    Next
    
    AddElement(LdbBdd())
    LdbBdd() = ttmp
    LDB_CountField()
    
  EndProcedure
  
  ;  Return the number of records
  Procedure LDB_CountRecord()
    
    If ListSize(LdbBdd())
      ProcedureReturn ListSize(LdbBdd()) - 1
    EndIf
    
  EndProcedure
  
  ;  (Internal procedure) Read the specified field in the current record ( include 0 ) 
  Procedure.s LDB_Rid(FieldID)
    
    Temp.s = StringField(LdbBdd(),FieldID, #Separator)
    ; Replace CRLF by #CRLFSubstitution
    Temp = ReplaceString(Temp, #CRLFSubstitution, #CRLF$)
    
    ProcedureReturn Temp
  EndProcedure
  
  ;  Read the specified field in the current record ( 0 return "" )
  Procedure.s LDB_Read(FieldID)
    
    xx.s = ""
    If LDB_GetPointer() <> 0 
      xx.s = LDB_Rid(FieldID) 
    EndIf
    
    ProcedureReturn xx.s
  EndProcedure
  
  ;  Write text in the field 'Field'
  Procedure LDB_Write(FieldID, Text.s)
    
    ; Verify if trying to overwrite the first record
    If LDB_GetPointer() <> 0
      
      ; Replace CRLF by #CRLFSubstitution
      Text = ReplaceString(Text,#CRLF$,#CRLFSubstitution)
      ClearList(LdbField())
      
      For n=1 To LdbFieldCount
        AddElement(LdbField())
        LdbField() = StringField(LdbBdd(),n,#Separator)
      Next
      
      SelectElement(LdbField(),FieldID - 1)
      LdbField() = Text
      LdbBdd() = ""
      
      ForEach LdbField()
        LdbBdd()+LdbField()+#Separator
      Next
      
    EndIf
    
  EndProcedure
  
  ;  ( Internal ) Add a record at the end of the database / set the pointer to this record
  Procedure LDB_AddRecord()
    
    LastElement(LdbBdd())
    AddElement(LdbBdd())
    
    For n=1 To LdbFieldCount
      LdbBdd()+#Separator
    Next
    
  EndProcedure
  
  ;  Delete the current record
  Procedure LDB_DeleteRecord()
    DeleteElement(LdbBdd(),1)
  EndProcedure
  
  ;  Write LinkedList to Disk
  Procedure.b LDB_SaveDataBase()
    
    PointerTemp = LDB_GetPointer()
    
    FileID = CreateFile(#PB_Any, LdbDatabaseFile)
    
    If IsFile(FileID)
      
      ForEach LdbBdd()
        WriteStringN(FileID, LdbBdd())
      Next
      
      CloseFile(FileID)
      
      LDB_SetPointer(PointerTemp)
      
      ProcedureReturn 1
      
    EndIf
    
  EndProcedure
  
  ;  Close the open Database
  Procedure LDB_CloseDataBase()
    
    ClearList(LdbBdd())
    ClearList(LdbField())
    
    LdbDatabaseFile.s = ""
    LdbFieldCount = 0
    LdbSearchStop = 0 
    LdbSearchReturn = 0
    LdbSearchField = 0 
    LdbSearchPointer = 0
    LdbSearchOption = 0 
    LdbSearchString.s = ""
    LdbFlag.b = 0
    
    If IsRegularExpression(Reg_Ex_IsNumeric)
      FreeRegularExpression(Reg_Ex_IsNumeric)
      Reg_Ex_IsNumeric = #Null
    EndIf
    
  EndProcedure
  
  ;  Set a field name
  Procedure LDB_SetFieldName(Nb,FieldName.s)
    
    tmp = LDB_GetPointer()
    LDB_SetPointer(0)
    LDB_Write(Nb, FieldName)
    LDB_SetPointer(tmp)
    
  EndProcedure
  
  ;  Get a field name
  Procedure.s LDB_GetFieldName(Nb)
    
    tmp = LDB_GetPointer()
    LDB_SetPointer(0)
    ttt.s = LDB_Rid(Nb)
    LDB_SetPointer(tmp)
    
    ProcedureReturn ttt
  EndProcedure
  
  ;  Set the pointer to the previous record
  Procedure LDB_PreviousRecord()
    
    PreviousElement(LdbBdd())
    
    If LDB_GetPointer() = 0 
      LDB_SetPointer(1) 
    EndIf
    
  EndProcedure
  
  ;  Set the pointer to the next record
  Procedure LDB_NextRecord()
    
    NextElement(LdbBdd())
    
  EndProcedure
  
  ;  Insert a record at position 'nb' / -1 add a record as the last record
  Procedure LDB_InsertRecord(Nb.l)
    
    If Nb=-1
      LDB_AddRecord()
    Else
      
      LDB_SetPointer(Nb)
      InsertElement(LdbBdd())
      
      For n=1 To LdbFieldCount
        LdbBdd()+#Separator
      Next
      
    EndIf
    
  EndProcedure
  
  ;  Add a Field as the last field
  Procedure LDB_AddField(FieldName.s)
    
    tmp = LDB_GetPointer()
    LdbFieldCount + 1
    
    ForEach LdbBdd()
      LdbBdd()+#Separator
    Next
    
    LDB_SetFieldName(LdbFieldCount,FieldName)
    LDB_SetPointer(tmp)
    
  EndProcedure
  
  ;  Delete a field in the database
  Procedure LDB_DeleteField(Nb.l)
    
    tmp = LDB_GetPointer()
    
    ForEach LdbBdd()
      ClearList(LdbField())
      ttmp.s=""
      For n=1 To LdbFieldCount
        If Nb=n  
          Continue 
        EndIf
        ttmp + StringField(LdbBdd(),n,#Separator)+#Separator
      Next
      
      LdbBdd() = ttmp
      
    Next
    
    LDB_CountField()
    LDB_SetPointer(tmp)
    
  EndProcedure  
  
  ;  Search with options ( #CaseInsensitive #EveryWhere )
  ;  Initialise a search in Ldb
  Procedure LDB_SearchInit(FieldID, SearchText.s,Option)
    
    LdbSearchPointer = 1
    LdbSearchString.s = SearchText.s
    LdbSearchOption = Option
    LdbSearchField = FieldID
    LdbSearchStop = 0
    LdbSearchReturn = 0
    
  EndProcedure
  
  ;  Search and return record that match ( 0 = There is no more record that match  )
  ;  You must stop call this function
  Procedure LDB_Search()
    
    ; If there is no records : Quit
    If LdbSearchPointer = 1 And LDB_CountRecord() = 0
      LdbSearchStop = 1
      LdbSearchReturn = 0
    EndIf
    
    LdbSearchReturn = 0
    
    If LdbSearchPointer > LDB_CountRecord() 
      LdbSearchStop = 1 
    EndIf
    
    
    If LdbSearchStop = 0
      
      For n = LdbSearchPointer To LDB_CountRecord()
        LDB_SetPointer(n)
        xxc.s = LDB_Read(LdbSearchField)
        xxs.s = LdbSearchString
        
        ; Search case Insensitive : Put all stirng to Upper Case
        If LdbSearchOption & #LDB_CASE_INSENSITIVE
          xxc = UCase(xxc)
          xxs = UCase(xxs)
        EndIf
        
        ; Flag Stop : Quit at the next loop
        If n = LDB_CountRecord() 
          LdbSearchStop = 1 
        EndIf
        
        If LdbSearchOption & #LDB_EVERYWHERE
          
          If FindString(xxc, xxs, 1) > 0 
            LdbSearchPointer = n + 1
            LdbSearchReturn = LdbSearchPointer - 1
            Break 
          EndIf
          
        Else
          
          If xxc = xxs 
            LdbSearchPointer = n + 1 
            LdbSearchReturn = LdbSearchPointer - 1
            Break 
          EndIf
          
        EndIf
        
      Next
      
    EndIf
    
    ProcedureReturn LdbSearchReturn
  EndProcedure
  
  ;  Sort the database assuming field as Numerical
  
  Procedure LDB_SortNum(FieldID)
    
    If Reg_Ex_IsNumeric = #Null
      Reg_Ex_IsNumeric = CreateRegularExpression(#PB_Any,"^[[:digit:].]+$") ; 
    EndIf
    
    If IsRegularExpression(Reg_Ex_IsNumeric)
      
      Nb = LDB_CountRecord()
      
      If Nb > 1 ; Si au moins 2 enregistrements
        
        PointerTemp = LDB_GetPointer()
        
        If MatchRegularExpression(Reg_Ex_IsNumeric, LDB_Read(FieldID))
          
          For n = 1 To Nb-1
            
            For i=n+1 To Nb
              
              LDB_SetPointer(n)
              FirstRecord.f = ValF(LDB_Read(FieldID))
              
              LDB_SetPointer(i)
              SecondRecord.f = ValF(LDB_Read(FieldID))
              
              If FirstRecord.f > SecondRecord.f
                
                SelectElement(LdbBdd(), n)
                *Temp00 = @LdbBdd()
                SelectElement(LdbBdd(), i)
                *Temp01 = @LdbBdd()
                SwapElements(LdbBdd(), *Temp00, *Temp01)
                
              EndIf
              
            Next i
          Next n
          
        EndIf
      EndIf
      
      LDB_SetPointer(PointerTemp)
     
    EndIf
    
  EndProcedure
    
    ;  Sort the database assuming field as String
    ;  Défault = CaseSensitive / Just 1 Option = #CaseInsensitive
    
  Procedure LDB_SortAlpha(FieldID, Option)
    
    Nb = LDB_CountRecord()
    
    If Nb > 1 ; There is at list 2 records
      
      PointerTemp = LDB_GetPointer()
      
      For n = 1 To Nb-1
        
        For i = n + 1 To Nb
          
          LDB_SetPointer(n)
          FirstRecord.s = LDB_Read(FieldID)
          
          LDB_SetPointer(i)
          SecondRecord.s = LDB_Read(FieldID)
          
          ; Pur all in UpperCase if #CaseInsensitive
          If Option = #LDB_CASE_INSENSITIVE
            FirstRecord = UCase(FirstRecord)
            SecondRecord = UCase(SecondRecord)
          EndIf
          
          If FirstRecord > SecondRecord
            SelectElement(LdbBdd(), n)
            *Temp00 = @LdbBdd()
            SelectElement(LdbBdd(), i)
            *Temp01 = @LdbBdd()
            SwapElements(LdbBdd(), *Temp00, *Temp01)
          EndIf
          
        Next i
        
      Next n
      
      LDB_SetPointer(PointerTemp)
      
    EndIf  
    
  EndProcedure
  
  Procedure.s LDB_FormatFields(Separator.s)
    
    For FieldID = 1 To LdbFieldCount
      
      Output.s + LDB_Read(FieldID)
      
      If FieldID < LdbFieldCount
        Output + Separator
      EndIf
      
    Next
    
    ProcedureReturn Output
  EndProcedure
  
EndModule

CompilerIf #PB_Compiler_IsMainFile
  
  ; F1 Test for the LDB library ( Little Database )
  ; launch it in debug mode
  
  If FileSize("Drivers.ldb") > 0
    DeleteFile("Drivers.ldb")
  EndIf
  
  ; Create a new Database with 3 fields
  LittleDataBase::LDB_CreateDataBase("Drivers.ldb","Birth Date,Name,Surname")
  
  ; Add one Record
  LittleDataBase::LDB_InsertRecord(-1)
  ; Write data to this record
  LittleDataBase::LDB_Write(1,"1969") ; 1st field
  LittleDataBase::LDB_Write(2,"schumacher") ; 2nd field
  LittleDataBase::LDB_Write(3,"Mikael")     ; 3rd field
  
  ; Add another Record
  LittleDataBase::LDB_InsertRecord(-1)
  ; Write data to this record
  LittleDataBase::LDB_Write(1,"1980") ; 1st field
  LittleDataBase::LDB_Write(2,"Button") ; 2nd field
  LittleDataBase::LDB_Write(3,"Jenson") ; 3rd field
  
  ; Add another Record
  LittleDataBase::LDB_InsertRecord(-1)
  ; Write data to this record
  LittleDataBase::LDB_Write(1,"1981") ; 1st field
  LittleDataBase::LDB_Write(2,"Alonso") ; 2nd field
  LittleDataBase::LDB_Write(3,"Fernando") ; 3rd field
  
  ; Add another Record
  LittleDataBase::LDB_InsertRecord(-1)
  ; Write data to this record
  LittleDataBase::LDB_Write(1,"1971") ; 1st field
  LittleDataBase::LDB_Write(2,"Villeneuve") ; 2nd field
  LittleDataBase::LDB_Write(3,"Jacques")    ; 3rd field
  
  ; Insert a record at 3rd position
  LittleDataBase::LDB_InsertRecord(3)
  ; Write data to this record
  LittleDataBase::LDB_Write(1,"1975") ; 1st field
  LittleDataBase::LDB_Write(2,"Schumacher") ; 2nd field
  LittleDataBase::LDB_Write(3,"Ralph")      ; 3rd field
  
  ; Sort the database by field 1 ( Birth Date )
  LittleDataBase::LDB_SortNum(1)
  
  ; Show all drivers sorted by birth Date
  Debug "Drivers sorted by birth date"
  
  For n=1 To LittleDataBase::LDB_CountRecord()
    LittleDataBase::LDB_SetPointer(n)
    Debug LittleDataBase::LDB_FormatFields(" ")
  Next
  
  Debug ""
  
  ; Sort the database by Drivers names
  LittleDataBase::LDB_SortAlpha(2,LittleDataBase::#LDB_CASE_INSENSITIVE)
  
  ; Show all drivers sorted by name
  Debug "Drivers sorted by name"
  
  For n=1 To LittleDataBase::LDB_CountRecord()
    LittleDataBase::LDB_SetPointer(n)
    Debug LittleDataBase::LDB_FormatFields(" ")
  Next
  
  Debug ""
  
  ; Search all name = Schumacher
  LittleDataBase::LDB_SearchInit(2,"Schumacher", LittleDataBase::#LDB_CASE_INSENSITIVE)
  
  ; Show all drivers = Schumacher
  Debug "Drivers with name = Schumacher"
  
  Repeat
    
    Champ = LittleDataBase::LDB_Search()
    
    If Champ = 0 
      Break 
    EndIf ; if 0 --> search finished
    
    LittleDataBase::LDB_SetPointer(Champ)
    Debug LittleDataBase::LDB_Read(1) + " " + LittleDataBase::LDB_Read(2) + " " + LittleDataBase::LDB_Read(3)
    
  ForEver
  
  Debug ""
  
  ; Database Infos
  Debug "Database Infos"
  Debug "Number of fields " + Str(LittleDataBase::LDB_CountField())
  Debug "Name of field"
  
  For n = 1 To LittleDataBase::LDB_CountField()
    Debug "Field n°" + Str(n) + " = " + LittleDataBase::LDB_GetFieldName(n)
  Next
  
  Debug "Number of records " + Str(LittleDataBase::LDB_CountRecord())
  
  ; Save Database to disk
  LittleDataBase::LDB_SaveDatabase()
  LittleDataBase::LDB_CloseDatabase()
  
  Debug ""
  Debug "--------------------------------------------------------"
  Debug "Loading from file test"
  Debug ""
  
  LittleDataBase::LDB_OpenDataBase("Drivers.ldb")
  
  LittleDataBase::LDB_SortNum(1)
  
  Debug "Drivers sorted by birth date"
  
  For n=1 To LittleDataBase::LDB_CountRecord()
    LittleDataBase::LDB_SetPointer(n)
    Debug LittleDataBase::LDB_FormatFields(" ")
  Next
  
  Debug ""
  Debug "Database Infos"
  Debug "Number of fields " + Str(LittleDataBase::LDB_CountField())
  Debug "Name of field"
  
  For n = 1 To LittleDataBase::LDB_CountField()
    Debug "Field n°" + Str(n) + " = " + LittleDataBase::LDB_GetFieldName(n)
  Next
  
  Debug "Number of records " + Str(LittleDataBase::LDB_CountRecord())
 
  ; Close the Database
  LittleDataBase::LDB_CloseDatabase()
  
CompilerEndIf 

; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<
Last edited by StarBootics on Sat Feb 14, 2015 5:20 pm, edited 1 time in total.
The Stone Age did not end due to a shortage of stones !
User avatar
Tenaja
Addict
Addict
Posts: 1959
Joined: Tue Nov 09, 2010 10:15 pm

Re: Droopy Lib - Little DataBase - Module Version

Post by Tenaja »

Thanks for sharing. I am not sure I will have a need for it, but I saved it just in case.
User avatar
StarBootics
Addict
Addict
Posts: 1006
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: Droopy Lib - Little DataBase - Module Version

Post by StarBootics »

@ Tenaja : You're welcome !

Now I'm wondering if I should upgrade this lib to have more than 1 table since I have found a way to do it. See the Window Module in the code here : http://www.purebasic.fr/english/viewtop ... 27&t=61655

What do you think ?

Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
User avatar
StarBootics
Addict
Addict
Posts: 1006
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: Droopy Lib - Little DataBase - Module Version

Post by StarBootics »

StarBootics wrote:Now I'm wondering if I should upgrade this lib to have more than 1 table since I have found a way to do it. See the Window Module in the code here : http://www.purebasic.fr/english/viewtop ... 27&t=61655
What do you think ?
Too late my self ...

This the version #2 of the "LittleDataBase - Module.pb" supporting multiple tables.
Have fun !

StarBootics

Code: Select all

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Project name : LittleDataBase - Module
; File Name : LittleDataBase - Module.pb
; File version: 2.0.0
; Programming : OK
; Programmed by : StarBootics
; Date : 14-02-2015
; Last Update : 14-02-2015
; PureBasic code : V5.31
; Platform : Windows, Linux, MacOS X
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

DeclareModule LittleDataBase
  
  #LDB_CASE_SENSITIVE = 0
  #LDB_CASE_INSENSITIVE = 1
  #LDB_EVERYWHERE = 2
  
  Declare LDB_CreateDataBase(Database.s)
  Declare LDB_CreateTable(TableName.s, Fields.s)
  Declare LDB_SelectTable(ByIndex.l, ByName.s = "", ByNameOption.l = #LDB_CASE_SENSITIVE)
  Declare.l LDB_GetCurrentTable()
  Declare LDB_SetCurrentTable(TableID.l)
  
  Declare.s LDB_GetTableName()
  Declare LDB_SetTableName(TableName.s)
  Declare LDB_FirstTable()
  Declare LDB_PreviousTable()
  Declare LDB_NextTable()
  
  Declare.l LDB_CountTables()
  Declare LDB_SaveDataBase()
  Declare LDB_OpenDataBase(DatabaseFile.s)
  Declare LDB_CloseDataBase()
  Declare LDB_GetPointer()
  
  Declare LDB_SetPointer(RecordID.l)
  Declare LDB_PreviousRecord()
  Declare LDB_NextRecord()
  Declare LDB_FirstRecord()
  Declare LDB_CountField()
  Declare LDB_InsertRecord(RecordID.l)
  
  Declare LDB_DeleteRecord()
  Declare LDB_CountRecord()
  Declare.s LDB_Read(FieldID.l)
  Declare LDB_Write(FieldID.l, Text.s)
  Declare LDB_SetFieldName(FieldID.l, FieldName.s)
  
  Declare.s LDB_GetFieldName(FieldID.l)
  Declare LDB_AddField(FieldName.s)
  Declare LDB_DeleteField(FieldID.l)
  Declare LDB_SearchInit(FieldID.l, SearchText.s, Option.l)
  Declare LDB_Search()
  
  Declare LDB_SortNum(FieldID.l)
  Declare LDB_SortAlpha(FieldID.l, Option.l)
  Declare.s LDB_FormatFields(Separator.s)
  
EndDeclareModule

Module LittleDataBase
  
  ;/ Constants
  #Separator = Chr(1)
  #FieldSeparator = ","
  #CRLFSubstitution = Chr(2)
  
  Structure LittleTable
    
    Name.s
    FieldCount.l
    SearchStop.l
    SearchReturn.l
    SearchField.l
    SearchPointer.l 
    SearchOption.l 
    SearchString.s
    
    List Bdd.s()
    List Field.s()
    
  EndStructure
  
  Global DatabaseFileName.s, Reg_Ex_IsNumeric.i, InitFlag.b
  Global NewList Tables.LittleTable()
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Private Functions <<<<<
  
  Procedure Private_SeachLittleTable(P_Name.s, Option.l)
    
    Protected IsFoundAtIndex.l = -1
    
    Select Option
        
      Case #LDB_CASE_SENSITIVE
        
        ForEach Tables()
          
          If Tables()\Name = P_Name
            IsFoundAtIndex = ListIndex(Tables())
            Break
          EndIf
          
        Next
        
      Case #LDB_CASE_INSENSITIVE
        
        ForEach Tables()
          
          If UCase(Tables()\Name) = UCase(P_Name)
            IsFoundAtIndex = ListIndex(Tables())
            Break
          EndIf
          
        Next
        
    EndSelect

    ProcedureReturn IsFoundAtIndex
  EndProcedure

  Procedure.s Private_LDB_Rid(FieldID)
    
    ;  (Internal procedure) Read the specified field in the current record ( include 0 ) 
    
    Temp.s = StringField(Tables()\Bdd(),FieldID, #Separator)
    ; Replace CRLF by #CRLFSubstitution
    Temp = ReplaceString(Temp, #CRLFSubstitution, #CRLF$)
    
    ProcedureReturn Temp
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Functions acting on the database itself <<<<<
  
  Procedure LDB_CreateDataBase(DatabaseFile.s)
    
    If InitFlag = #True
      LDB_CloseDataBase()
    EndIf
   
    DatabaseFileName = DatabaseFile
    InitFlag = #True
    
  EndProcedure
  
  Procedure LDB_CreateTable(TableName.s, Fields.s)
    
    ; We search in the tables list if the "TableName"
    ; is not already present. If not, we add it to the 
    ; database.
    
    If Private_SeachLittleTable(TableName, #LDB_CASE_INSENSITIVE) = -1
      
      AddElement(Tables())
      Tables()\Name = TableName

      ttmp.s = ""
      maxfields = CountString(Fields, #FieldSeparator) + 1
      
      For n=1 To maxfields
        ttmp + StringField(Fields, n, #FieldSeparator)
        If n <> maxfields
          ttmp + #Separator
        EndIf
      Next
      
      AddElement(Tables()\Bdd())
      Tables()\Bdd() = ttmp
      
      LDB_CountField()

    EndIf
    
  EndProcedure
  
  Procedure LDB_SelectTable(ByIndex.l, ByName.s = "", ByNameOption.l = #LDB_CASE_SENSITIVE) ; Select table ByIndex or ByName
    
    ; Please note, when you select table by name the ByIndex is ignored
    
    If ByName <> ""
      If Private_SeachLittleTable(ByName, ByNameOption) <> -1
        Success = 1
      EndIf
    Else
      If SelectElement(Tables(), ByIndex)
        Success = 1
      EndIf
    EndIf
    
    ProcedureReturn Success
  EndProcedure
  
  Procedure.l LDB_GetCurrentTable() ; Get the index of the current table
    
    If ListSize(Tables())
      ProcedureReturn ListIndex(Tables())
    EndIf
    
  EndProcedure
  
  Procedure LDB_SetCurrentTable(TableID.l) ; Set the index of the current table
    
    If ListSize(Tables())
      SelectElement(Tables(), TableID)
    EndIf
    
  EndProcedure
  
  Procedure LDB_FirstTable()
    
    FirstElement(Tables())
    
  EndProcedure
  
  Procedure LDB_PreviousTable()
    
    PreviousElement(Tables())
   
  EndProcedure
  
  Procedure LDB_NextTable()
    
    NextElement(Tables())
    
  EndProcedure
  
  Procedure.l LDB_CountTables()
    
    If ListSize(Tables())
      ProcedureReturn ListSize(Tables())
    EndIf
    
  EndProcedure
  
  Procedure.s LDB_GetTableName()
    
    ProcedureReturn Tables()\Name
  EndProcedure
  
  Procedure LDB_SetTableName(TableName.s)
    
    Tables()\Name = TableName
    
  EndProcedure
 
  Procedure LDB_SaveDataBase()
    
    TableTemp = LDB_GetCurrentTable()
    PointerTemp = LDB_GetPointer()
    
    FileID = CreateFile(#PB_Any, DatabaseFileName)
    
    If IsFile(FileID)
      
      ForEach Tables()
        
        WriteStringN(FileID, "LittleTable:" + Tables()\Name)
        
        ForEach Tables()\Bdd()
          WriteStringN(FileID, Tables()\Bdd())
        Next
      Next
      
      CloseFile(FileID)
      
      LDB_SetCurrentTable(TableTemp)
      LDB_SetPointer(PointerTemp)
      
      ProcedureReturn 1
      
    EndIf
    
  EndProcedure
  
  Procedure LDB_OpenDataBase(DatabaseFile.s) ;  Open an existing Database ( Return 1 if success / 0 if fail ) Thanks to Baldrick
    
    LDB_CreateDataBase(DatabaseFile)
    
    FileID = ReadFile(#PB_Any, DatabaseFileName)
    
    If IsFile(FileID)
      
      Retour = 1
      
      While Eof(FileID) = 0
        
        DataBaseLine.s = ReadString(FileID)
        
        If FindString(DataBaseLine, "LittleTable:")
          AddElement(Tables())
          Tables()\Name = StringField(DataBaseLine, 2, ":")
        Else
          AddElement(Tables()\Bdd())
          Tables()\Bdd() = DataBaseLine
        EndIf
        
      Wend
      
      CloseFile(FileID)
      
      ForEach Tables()
        LDB_CountField()
      Next
      
    EndIf
    
    ProcedureReturn Retour
  EndProcedure
  
  Procedure LDB_CloseDataBase()
    
    ForEach Tables()
      ClearList(Tables()\Bdd())
      ClearList(Tables()\Field())
    Next
    
    ClearList(Tables())
    
    DatabaseFileName = ""
    Flag = #False
    
    If IsRegularExpression(Reg_Ex_IsNumeric)
      FreeRegularExpression(Reg_Ex_IsNumeric)
      Reg_Ex_IsNumeric = #Null
    EndIf
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Functions acting on the current table (Pubilc ) <<<<<
  
  Procedure LDB_GetPointer() ; Get the index of the current record
    
    If ListSize(Tables()\Bdd())
      ProcedureReturn ListIndex(Tables()\Bdd())
    EndIf
    
  EndProcedure
  
  Procedure LDB_SetPointer(RecordID.l) ; Set the index of the current record
    
    If ListSize(Tables()\Bdd())
      SelectElement(Tables()\Bdd(), RecordID)
    EndIf
    
  EndProcedure
  
  Procedure LDB_FirstRecord() ;  Set the pointer to the next record
    
    LDB_SetPointer(1) 
    
  EndProcedure
  
  Procedure LDB_PreviousRecord() ;  Set the pointer to the previous record
    
    PreviousElement(Tables()\Bdd())
    
    If LDB_GetPointer() = 0 
      LDB_SetPointer(1) 
    EndIf
    
  EndProcedure
  
  Procedure LDB_NextRecord() ;  Set the pointer to the next record
    
    NextElement(Tables()\Bdd())
    
  EndProcedure
  
  Procedure LDB_CountField()
    
    If ListSize(Tables()\Bdd())
      
      tmp = LDB_GetPointer()
      FirstElement(Tables()\Bdd())
      Tables()\FieldCount = CountString(Tables()\Bdd(),#Separator) + 1
      LDB_SetPointer(tmp)
      
      ProcedureReturn Tables()\FieldCount
      
    EndIf
    
  EndProcedure
  
  Procedure LDB_InsertRecord(RecordID.l);  Insert a record at position 'nb' / -1 add a record as the last record
    
    If RecordID = -1
      AddElement(Tables()\Bdd())
    Else
      LDB_SetPointer(RecordID)
      InsertElement(Tables()\Bdd())
    EndIf
    
    For n=1 To Tables()\FieldCount
      Tables()\Bdd() + #Separator
    Next
    
  EndProcedure
  
  Procedure LDB_DeleteRecord() ;  Delete the current record
    
    DeleteElement(Tables()\Bdd(), 1)
    
  EndProcedure
  
  Procedure LDB_CountRecord()
    
    If ListSize(Tables()\Bdd())
      ProcedureReturn ListSize(Tables()\Bdd()) - 1
    EndIf
    
  EndProcedure
  
  Procedure.s LDB_Read(FieldID.l) ;  Read the specified field in the current record ( 0 return "" )
    
    xx.s = ""
    
    If LDB_GetPointer() <> 0 
      xx.s = Private_LDB_Rid(FieldID)
    EndIf
    
    ProcedureReturn xx
  EndProcedure
  
  Procedure LDB_Write(FieldID.l, Text.s) ;  Write text in the field 'Field'
    
    ; Verify if trying to overwrite the first record
    If LDB_GetPointer() <> 0
      
      ; Replace CRLF by #CRLFSubstitution
      Text = ReplaceString(Text,#CRLF$,#CRLFSubstitution)
      ClearList(Tables()\Field())
      
      For n=1 To Tables()\FieldCount
        AddElement(Tables()\Field())
        Tables()\Field() = StringField(Tables()\Bdd(),n,#Separator)
      Next
      
      SelectElement(Tables()\Field(), FieldID - 1)
      Tables()\Field() = Text
      Tables()\Bdd() = ""
      
      ForEach Tables()\Field()
        Tables()\Bdd() + Tables()\Field() + #Separator
      Next
      
    EndIf
    
  EndProcedure
  
  Procedure LDB_SetFieldName(FieldID.l, FieldName.s) ;  Set a field name
    
    tmp = LDB_GetPointer()
    LDB_SetPointer(0)
    LDB_Write(FieldID, FieldName)
    LDB_SetPointer(tmp)
    
  EndProcedure
  
  Procedure.s LDB_GetFieldName(FieldID.l) ;  Get a field name
    
    tmp = LDB_GetPointer()
    LDB_SetPointer(0)
    ttt.s = Private_LDB_Rid(FieldID)
    LDB_SetPointer(tmp)
    
    ProcedureReturn ttt
  EndProcedure
  
  Procedure LDB_AddField(FieldName.s) ;  Add a Field as the last field
    
    tmp = LDB_GetPointer()
    Tables()\FieldCount + 1
    
    ForEach Tables()\Bdd()
      Tables()\Bdd() + #Separator
    Next
    
    LDB_SetFieldName(Tables()\FieldCount, FieldName)
    LDB_SetPointer(tmp)
    
  EndProcedure
  
  Procedure LDB_DeleteField(FieldID.l) ;  Delete a field in the database
    
    tmp = LDB_GetPointer()
    
    ForEach Tables()\Bdd()
      ;ClearList(Tables()\Field())
      
      ttmp.s=""
      
      For n=1 To Tables()\FieldCount
        
        If FieldID <> n  
          ttmp + StringField(Tables()\Bdd(), n, #Separator) + #Separator
        EndIf
        
      Next
      
      Tables()\Bdd() = ttmp
      
    Next
    
    LDB_CountField()
    LDB_SetPointer(tmp)
    
  EndProcedure  
  
  Procedure LDB_SearchInit(FieldID.l, SearchText.s, Option.l)
    ;  Search with options ( #CaseInsensitive #EveryWhere )
    ;  Initialise a search in Ldb
    Tables()\SearchPointer = 1
    Tables()\SearchString = SearchText.s
    Tables()\SearchOption = Option
    Tables()\SearchField = FieldID
    Tables()\SearchStop = 0
    Tables()\SearchReturn = 0
    
  EndProcedure
  
  Procedure LDB_Search()
  ;  Search and return record that match ( 0 = There is no more record that match  )
  ;  You must stop call this function
    
    ; If there is no records : Quit
    If Tables()\SearchPointer = 1 And LDB_CountRecord() = 0
      Tables()\SearchStop = 1
      Tables()\SearchReturn = 0
    EndIf
    
    Tables()\SearchReturn = 0
    
    If Tables()\SearchPointer > LDB_CountRecord() 
      Tables()\SearchStop = 1 
    EndIf
    
    If Tables()\SearchStop = 0
      
      For n = Tables()\SearchPointer To LDB_CountRecord()
        LDB_SetPointer(n)
        xxc.s = LDB_Read(Tables()\SearchField)
        xxs.s = Tables()\SearchString
        
        ; Search case Insensitive : Put all stirng to Upper Case
        If Tables()\SearchOption & #LDB_CASE_INSENSITIVE
          xxc = UCase(xxc)
          xxs = UCase(xxs)
        EndIf
        
        ; Flag Stop : Quit at the next loop
        If n = LDB_CountRecord() 
          Tables()\SearchStop = 1 
        EndIf
        
        If Tables()\SearchOption & #LDB_EVERYWHERE
          
          If FindString(xxc, xxs, 1) > 0 
            Tables()\SearchPointer = n + 1
            Tables()\SearchReturn = Tables()\SearchPointer - 1
            Break 
          EndIf
          
        Else
          
          If xxc = xxs 
            Tables()\SearchPointer = n + 1 
            Tables()\SearchReturn = Tables()\SearchPointer - 1
            Break 
          EndIf
          
        EndIf
        
      Next
      
    EndIf
    
    ProcedureReturn Tables()\SearchReturn
  EndProcedure
  
  Procedure LDB_SortNum(FieldID.l) ;  Sort the database assuming field as Numerical
    
    If Reg_Ex_IsNumeric = #Null
      Reg_Ex_IsNumeric = CreateRegularExpression(#PB_Any,"^[[:digit:].]+$") ; 
    EndIf
    
    If IsRegularExpression(Reg_Ex_IsNumeric)
      
      Nb = LDB_CountRecord()
      
      If Nb > 1 ; Si au moins 2 enregistrements
        
        PointerTemp = LDB_GetPointer()
        
        If MatchRegularExpression(Reg_Ex_IsNumeric, LDB_Read(FieldID))
          
          For n = 1 To Nb-1
            
            For i=n+1 To Nb
              
              LDB_SetPointer(n)
              FirstRecord.f = ValF(LDB_Read(FieldID))
              
              LDB_SetPointer(i)
              SecondRecord.f = ValF(LDB_Read(FieldID))
              
              If FirstRecord.f > SecondRecord.f
                
                SelectElement(Tables()\Bdd(), n)
                *Temp00 = @Tables()\Bdd()
                SelectElement(Tables()\Bdd(), i)
                *Temp01 = @Tables()\Bdd()
                SwapElements(Tables()\Bdd(), *Temp00, *Temp01)
                
              EndIf
              
            Next i
          Next n
          
        EndIf
      EndIf
      
      LDB_SetPointer(PointerTemp)
     
    EndIf
    
  EndProcedure
    
  Procedure LDB_SortAlpha(FieldID.l, Option.l) ;  Sort the database assuming field as String
        
    ;  Défault = CaseSensitive / Just 1 Option = #CaseInsensitive
  
    Nb = LDB_CountRecord()
    
    If Nb > 1 ; There is at list 2 records
      
      PointerTemp = LDB_GetPointer()
      
      For n = 1 To Nb-1
        
        For i = n + 1 To Nb
          
          LDB_SetPointer(n)
          FirstRecord.s = LDB_Read(FieldID)
          
          LDB_SetPointer(i)
          SecondRecord.s = LDB_Read(FieldID)
          
          ; Pur all in UpperCase if #CaseInsensitive
          If Option = #LDB_CASE_INSENSITIVE
            FirstRecord = UCase(FirstRecord)
            SecondRecord = UCase(SecondRecord)
          EndIf
          
          If FirstRecord > SecondRecord
            SelectElement(Tables()\Bdd(), n)
            *Temp00 = @Tables()\Bdd()
            SelectElement(Tables()\Bdd(), i)
            *Temp01 = @Tables()\Bdd()
            SwapElements(Tables()\Bdd(), *Temp00, *Temp01)
          EndIf
          
        Next i
        
      Next n
      
      LDB_SetPointer(PointerTemp)
      
    EndIf  
    
  EndProcedure
  
  Procedure.s LDB_FormatFields(Separator.s)
    
    For FieldID = 1 To Tables()\FieldCount
      
      Output.s + LDB_Read(FieldID)
      
      If FieldID < Tables()\FieldCount
        Output + Separator
      EndIf
      
    Next
    
    ProcedureReturn Output
  EndProcedure

EndModule

CompilerIf #PB_Compiler_IsMainFile
  
  ; F1 Test for the LDB library ( Little Database )
  ; launch it in debug mode
  
  If FileSize("F1 Racing.ldb") > 0
    DeleteFile("F1 Racing.ldb")
  EndIf
  
  ; Create a new Database with 3 fields
  LittleDataBase::LDB_CreateDataBase("F1 Racing.ldb")
  LittleDataBase::LDB_CreateTable("Drivers","Birth Date,Name,Surname")
  
;   ; Add one Record
  LittleDataBase::LDB_InsertRecord(-1)
  ; Write data to this record
  LittleDataBase::LDB_Write(1,"1969") ; 1st field
  LittleDataBase::LDB_Write(2,"schumacher") ; 2nd field
  LittleDataBase::LDB_Write(3,"Mikael")     ; 3rd field
  
  ; Add another Record
  LittleDataBase::LDB_InsertRecord(-1)
  ; Write data to this record
  LittleDataBase::LDB_Write(1,"1980") ; 1st field
  LittleDataBase::LDB_Write(2,"Button") ; 2nd field
  LittleDataBase::LDB_Write(3,"Jenson") ; 3rd field
  
  ; Add another Record
  LittleDataBase::LDB_InsertRecord(-1)
  ; Write data to this record
  LittleDataBase::LDB_Write(1,"1981") ; 1st field
  LittleDataBase::LDB_Write(2,"Alonso") ; 2nd field
  LittleDataBase::LDB_Write(3,"Fernando") ; 3rd field
  
  ; Add another Record
  LittleDataBase::LDB_InsertRecord(-1)
  ; Write data to this record
  LittleDataBase::LDB_Write(1,"1971") ; 1st field
  LittleDataBase::LDB_Write(2,"Villeneuve") ; 2nd field
  LittleDataBase::LDB_Write(3,"Jacques")    ; 3rd field
  
  ; Insert a record at 3rd position
  LittleDataBase::LDB_InsertRecord(3)
  ; Write data to this record
  LittleDataBase::LDB_Write(1,"1975") ; 1st field
  LittleDataBase::LDB_Write(2,"Schumacher") ; 2nd field
  LittleDataBase::LDB_Write(3,"Ralph")      ; 3rd field
  
  
  LittleDataBase::LDB_CreateTable("Cars","Frame,Engine")
  
  LittleDataBase::LDB_InsertRecord(-1)
  LittleDataBase::LDB_Write(1,"Ferrari") ; 1st field
  LittleDataBase::LDB_Write(2,"Ferrari") ; 2nd field
  
  LittleDataBase::LDB_InsertRecord(-1)
  LittleDataBase::LDB_Write(1,"Williams") ; 1st field
  LittleDataBase::LDB_Write(2,"Renault") ; 2nd field
  
  LittleDataBase::LDB_InsertRecord(-1)
  LittleDataBase::LDB_Write(1,"Jordan") ; 1st field
  LittleDataBase::LDB_Write(2,"Peugot") ; 2nd field
  
  
  ; now we return to the Drivers table
  LittleDataBase::LDB_SelectTable(-1, "Drivers")
  
  
  ; Sort the database by field 1 ( Birth Date )
  LittleDataBase::LDB_SortNum(1)
  
  ; Show all drivers sorted by birth Date
  Debug "Drivers sorted by birth date"
  
  For n=1 To LittleDataBase::LDB_CountRecord()
    LittleDataBase::LDB_SetPointer(n)
    Debug LittleDataBase::LDB_FormatFields(" ")
  Next
  
  Debug ""
  
  ; Sort the database by Drivers names
  LittleDataBase::LDB_SortAlpha(2,LittleDataBase::#LDB_CASE_INSENSITIVE)
  
  ; Show all drivers sorted by name
  Debug "Drivers sorted by name"
  
  For n=1 To LittleDataBase::LDB_CountRecord()
    LittleDataBase::LDB_SetPointer(n)
    Debug LittleDataBase::LDB_FormatFields(" ")
  Next
  
  Debug ""
  
  ; Search all name = Schumacher
  LittleDataBase::LDB_SearchInit(2,"Schumacher", LittleDataBase::#LDB_CASE_INSENSITIVE)
  
  ; Show all drivers = Schumacher
  Debug "Drivers with name = Schumacher"
  
  Repeat
    
    Champ = LittleDataBase::LDB_Search()
    
    If Champ = 0 
      Break 
    EndIf ; if 0 --> search finished
    
    LittleDataBase::LDB_SetPointer(Champ)
    Debug LittleDataBase::LDB_Read(1) + " " + LittleDataBase::LDB_Read(2) + " " + LittleDataBase::LDB_Read(3)
    
  ForEver
  
  Debug ""
  
  ; Database Infos
  Debug "Database Infos"
  
  Debug "Number of Tables " + Str(LittleDataBase::LDB_CountTables())
  
  LittleDataBase::LDB_FirstTable()
  
  For TableID = 1 To LittleDataBase::LDB_CountTables()
    
    Debug "Table n°" + Str(TableID) + " = " + LittleDataBase::LDB_GetTableName()
    Debug "Number of fields " + Str(LittleDataBase::LDB_CountField())
    Debug "Name of field"
    
    For n = 1 To LittleDataBase::LDB_CountField()
      Debug "Field n°" + Str(n) + " = " + LittleDataBase::LDB_GetFieldName(n)
    Next
    
    Debug "Number of records " + Str(LittleDataBase::LDB_CountRecord())
    Debug ""
    
    LittleDataBase::LDB_NextTable()
    
  Next
  
  Debug "Database Content"
  Debug ""
  
  LittleDataBase::LDB_FirstTable()
  
  For TableID = 1 To LittleDataBase::LDB_CountTables()

    Debug "Table n°" + Str(TableID) + " = " + LittleDataBase::LDB_GetTableName()
    
    LittleDataBase::LDB_FirstRecord()
    
    For RecordID = 1 To LittleDataBase::LDB_CountRecord()
      Debug LittleDataBase::LDB_FormatFields(" ")
      LittleDataBase::LDB_NextRecord()
    Next
    
    LittleDataBase::LDB_NextTable()
    Debug ""
    
  Next
  
  ; Save Database to disk
  LittleDataBase::LDB_SaveDatabase()
  LittleDataBase::LDB_CloseDatabase()
  
  Debug "--------------------------------------------------------"
  Debug "Loading from file test"
  Debug ""
  
  LittleDataBase::LDB_OpenDataBase("F1 Racing.ldb")
  
  Debug "Database Content"
  Debug ""
  
  LittleDataBase::LDB_FirstTable()
  
  For TableID = 1 To LittleDataBase::LDB_CountTables()

    Debug "Table n°" + Str(TableID) + " = " + LittleDataBase::LDB_GetTableName()
    
    For RecordID = 1 To LittleDataBase::LDB_CountRecord()
      LittleDataBase::LDB_SetPointer(RecordID)
      Debug LittleDataBase::LDB_FormatFields(" ")
    Next
    
    LittleDataBase::LDB_NextTable()
    
    Debug ""
  Next
  
CompilerEndIf 

; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<
The Stone Age did not end due to a shortage of stones !
User avatar
Vera
Addict
Addict
Posts: 858
Joined: Tue Aug 11, 2009 1:56 pm
Location: Essen (Germany)

Re: Droopy Lib - Little DataBase - Module Version

Post by Vera »

Hello StarBootics,

I really appreciate your recent updates ... and looking forward how your series continues :-)

Now for your DataBase ~ I'm sure it'll run faster if Michael's name is corrected :mrgreen:

:wink:
User avatar
StarBootics
Addict
Addict
Posts: 1006
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: Droopy Lib - Little DataBase - Module Version

Post by StarBootics »

Vera wrote:Now for your DataBase ~ I'm sure it'll run faster if Michael's name is corrected :mrgreen:
Ah, Ah, Ah ! .. Elle est bien bonne ! :mrgreen:
I was just too lasy to correct the original example.

Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
User avatar
StarBootics
Addict
Addict
Posts: 1006
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: Droopy Lib - Little DataBase - OOP Version

Post by StarBootics »

Hello everyone,

Sorry to re-open an old topic but I have manage to convert my version of Droopy's Little DataBase from a Module to OOP style, made few modifications and added a functionality (Table removal from the Database)

Best regards
StarBootics

Code: Select all

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Project name : LittleDataBase
; File Name : LittleDataBase - OOP.pb
; File version: 1.0.0
; Programming : OK
; Programmed by : StarBootics
; Date : February 25th, 2022
; Last Update : March 7th, 2022
; PureBasic code : V6.00 Beta 5
; Platform : Windows, Linux, MacOS X
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Programming Notes
;
; Based on Droopy's LittleDataBase original code. 
; Changes made :
;
; - Conversion to OOP style.
; - Few methods renamed.
; - Methods about database filename manipulation added.
; - Methods about tables name manipulation added.
; - Methods about fields name manipulation added.
; - Method to suppress a table from the database added.
;
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

DeclareModule LittleDataBase
  
  #CASE_SENSITIVE = 0
  #CASE_INSENSITIVE = 1
  #EVERYWHERE = 2
  
  Interface LittleDataBase
    
    GetFileName.s()
    GetCurrentTable.i()
    GetCurrentRecord.i()
    GetTableName.s()
    GetFieldName.s(FieldID.i)
    SetFileName(FileName.s)
    SetCurrentTable(TableID.i)
    SetCurrentRecord(RecordID.i)
    SetTableName(TableName.s)
    SetFieldName(FieldID.i, FieldName.s)
    AddField(FieldName.s)
    DeleteField(FieldID.i)
    CreateTable(TableName.s, Fields.s)
    SelectTable.i(ByIndex.l, ByName.s = "", ByNameOption.l = #CASE_SENSITIVE)
    FirstTable()
    PreviousTable()
    NextTable()
    CountTables.i()
    DeleteTable()
    CountFields.i()
    CountRecords.i()
    InsertRecord(RecordID.i)
    DeleteRecord()
    FirstRecord()
    PreviousRecord()
    NextRecord()
    ReadRecord.s(FieldID.i)
    WriteRecord(FieldID.i, Text.s)
    SearchInit(FieldID.i, SearchText.s, Option.i)
    Search.i()
    SortNumerical(FieldID.i) 
    SortString(FieldID.i, Option.i)
    FormatFields.s(Separator.s)
    Save.i()
    Open.i()
    Close()
    
    Free()
    
  EndInterface
  
  Declare.i New(FileName.s)
  
EndDeclareModule

Module LittleDataBase
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Constants Declaration <<<<<
  
  #Separator = Chr(1)
  #FieldSeparator = ","
  #CRLFSubstitution = Chr(2)
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Structures Declaration <<<<<
  
  Structure Table
    
    Name.s
    FieldCount.l
    SearchStop.i
    SearchReturn.i
    SearchField.i
    SearchPointer.i 
    SearchOption.i
    SearchString.s
    
    List Bdd.s()
    List Field.s()
    
  EndStructure
  
  Structure Private_Members
    
    VirtualTable.i
    FileName.s
    RegexIsNumeric.i
    List Tables.Table()
    
  EndStructure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Private Instructions <<<<<
  
  Procedure.i Private_SearchTable(*This.Private_Members, Name.s, Option.i)
    
    Protected IsFoundAtIndex.i = -1
    
    Select Option
        
      Case #CASE_SENSITIVE
        
        ForEach *This\Tables()
          
          If *This\Tables()\Name = Name
            IsFoundAtIndex = ListIndex(*This\Tables())
            Break
          EndIf
          
        Next
        
      Case #CASE_INSENSITIVE
        
        ForEach *This\Tables()
          
          If UCase(*This\Tables()\Name) = UCase(Name)
            IsFoundAtIndex = ListIndex(*This\Tables())
            Break
          EndIf
          
        Next
        
    EndSelect

    ProcedureReturn IsFoundAtIndex
  EndProcedure
  
  Procedure.s Private_Rid(*This.Private_Members, FieldID.i)
    
    ;  (Internal procedure) Read the specified field in the current record ( include 0 ) 
    
    ProcedureReturn ReplaceString(StringField(*This\Tables()\Bdd(), FieldID, #Separator), #CRLFSubstitution, #CRLF$)
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Getters <<<<<

  Procedure.s GetFileName(*This.Private_Members)
    
    ProcedureReturn *This\FileName
  EndProcedure
  
  Procedure.i GetCurrentTable(*This.Private_Members) ; Get the index of the current table
    
    If ListSize(*This\Tables())
      ProcedureReturn ListIndex(*This\Tables())
    EndIf
    
  EndProcedure
  
  Procedure.i GetCurrentRecord(*This.Private_Members) ; Get the index of the current record
    
    If ListSize(*This\Tables()\Bdd())
      ProcedureReturn ListIndex(*This\Tables()\Bdd())
    EndIf
    
  EndProcedure
  
  Procedure.s GetTableName(*This.Private_Members)
    
    ProcedureReturn *This\Tables()\Name
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Setters <<<<<
  
  Procedure SetFileName(*This.Private_Members, FileName.s)
    
    *This\FileName = FileName
    
  EndProcedure

  Procedure SetCurrentTable(*This.Private_Members, TableID.i) ; Set the index of the current table
    
    If ListSize(*This\Tables())
      SelectElement(*This\Tables(), TableID)
    EndIf
    
  EndProcedure
  
  Procedure SetCurrentRecord(*This.Private_Members, RecordID.i) ; Set the index of the current record
    
    If ListSize(*This\Tables()\Bdd())
      SelectElement(*This\Tables()\Bdd(), RecordID)
    EndIf
    
  EndProcedure
  
  Procedure SetTableName(*This.Private_Members, TableName.s)
    
    If TableName <> ""
      *This\Tables()\Name = TableName
    EndIf
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The CountFields operator <<<<<
  
  Procedure.i CountFields(*This.Private_Members)
    
    If ListSize(*This\Tables()\Bdd())
      
      tmp = GetCurrentRecord(*This)
      FirstElement(*This\Tables()\Bdd())
      *This\Tables()\FieldCount = CountString(*This\Tables()\Bdd(), #Separator) + 1
      Count.i = *This\Tables()\FieldCount
      SetCurrentRecord(*This, tmp)
      
      ProcedureReturn Count
      
    EndIf
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The CountRecords operator <<<<<
  
  Procedure.i CountRecords(*This.Private_Members)
    
    If ListSize(*This\Tables()\Bdd())
      ProcedureReturn ListSize(*This\Tables()\Bdd()) - 1
    EndIf
    
  EndProcedure

  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The CreateTable operator <<<<<

  Procedure CreateTable(*This.Private_Members, TableName.s, Fields.s)
    
    ; We search in the tables list if the "TableName"
    ; is not already present. If not, we add it to the 
    ; database.
    
    If Private_SearchTable(*This, TableName, #CASE_INSENSITIVE) = -1
      
      AddElement(*This\Tables())
      
      *This\Tables()\Name = TableName
      
      ttmp.s = ""
      maxfields = CountString(Fields, #FieldSeparator) + 1
      
      For n = 1 To maxfields
        
        ttmp + StringField(Fields, n, #FieldSeparator)
        
        If n <> maxfields
          ttmp + #Separator
        EndIf
        
      Next
      
      AddElement(*This\Tables()\Bdd())
      *This\Tables()\Bdd() = ttmp
      
      CountFields(*This)
      
    EndIf
    
  EndProcedure

  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The SelectTable operator <<<<<

  Procedure.i SelectTable(*This.Private_Members, ByIndex.l, ByName.s = "", ByNameOption.l = #CASE_SENSITIVE) ; Select table ByIndex or ByName
    
    ; Please note, when you select table by name the ByIndex is ignored
    
    If ByName <> ""
      If Private_SearchTable(*This, ByName, ByNameOption) <> -1
        Success = 1
      EndIf
    Else
      If SelectElement(*This\Tables(), ByIndex)
        Success = 1
      EndIf
    EndIf
    
    ProcedureReturn Success
  EndProcedure

  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The FirstTable operator <<<<<
  
  Procedure FirstTable(*This.Private_Members)
    
    FirstElement(*This\Tables())
    
  EndProcedure

  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The PreviousTable operator <<<<<
  
  Procedure PreviousTable(*This.Private_Members)
    
    PreviousElement(*This\Tables())
   
  EndProcedure

  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The NextTable operator <<<<<
  
  Procedure NextTable(*This.Private_Members)
    
    NextElement(*This\Tables())
    
  EndProcedure

  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The CountTables operator <<<<<
  
  Procedure.i CountTables(*This.Private_Members)
    
    If ListSize(*This\Tables())
      ProcedureReturn ListSize(*This\Tables())
    EndIf
    
  EndProcedure

  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The DeleteTable operator <<<<<
  
  Procedure DeleteTable(*This.Private_Members) ;  Delete the current table
    
    DeleteElement(*This\Tables(), 1)
    
  EndProcedure

  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The InsertRecord operator <<<<<
  
  Procedure InsertRecord(*This.Private_Members, RecordID.i);  Insert a record at position 'nb' / -1 add a record as the last record
    
    If RecordID = -1
      LastElement(*This\Tables()\Bdd())
      AddElement(*This\Tables()\Bdd())
    Else
      SetCurrentRecord(*This, RecordID)
      InsertElement(*This\Tables()\Bdd())
    EndIf
    
    For n = 1 To *This\Tables()\FieldCount
      *This\Tables()\Bdd() + #Separator
    Next
    
  EndProcedure

  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The DeleteRecord operator <<<<<
  
  Procedure DeleteRecord(*This.Private_Members) ;  Delete the current record
    
    DeleteElement(*This\Tables()\Bdd(), 1)
    
  EndProcedure

  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The FirstRecord operator <<<<<
  
  Procedure FirstRecord(*This.Private_Members) ;  Set the pointer to the next record
    
    SetCurrentRecord(*This, 1) 
    
  EndProcedure

  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The PreviousRecord operator <<<<<
  
  Procedure PreviousRecord(*This.Private_Members) ;  Set the pointer to the previous record
    
    PreviousElement(*This\Tables()\Bdd())
    
    If GetCurrentRecord(*This) = 0 
      SetCurrentRecord(*This, 1) 
    EndIf
    
  EndProcedure

  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The NextRecord operator <<<<<
  
  Procedure NextRecord(*This.Private_Members) ;  Set the pointer to the next record
    
    NextElement(*This\Tables()\Bdd())
    
  EndProcedure

  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The ReadRecord operator <<<<<
  
  Procedure.s ReadRecord(*This.Private_Members, FieldID.i) ;  Read the specified field in the current record ( 0 return "" )
    
    If GetCurrentRecord(*This) <> 0 
      xx.s = Private_Rid(*This, FieldID)
    EndIf
    
    ProcedureReturn xx
  EndProcedure

  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The WriteRecord operator <<<<<
  
  Procedure WriteRecord(*This.Private_Members, FieldID.i, Text.s) ;  Write text in the field 'Field'
    
    ; Verify if trying to overwrite the first record
    If GetCurrentRecord(*This) <> 0
      
      ; Replace CRLF by #CRLFSubstitution
      Text = ReplaceString(Text, #CRLF$, #CRLFSubstitution)
      ClearList(*This\Tables()\Field())
      
      For n = 1 To *This\Tables()\FieldCount
        AddElement(*This\Tables()\Field())
        *This\Tables()\Field() = StringField(*This\Tables()\Bdd(), n, #Separator)
      Next
      
      SelectElement(*This\Tables()\Field(), FieldID - 1)
      *This\Tables()\Field() = Text
      *This\Tables()\Bdd() = ""
      
      ForEach *This\Tables()\Field()
        *This\Tables()\Bdd() + *This\Tables()\Field() + #Separator
      Next
      
    EndIf
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Getters (Extra) <<<<<
  
  Procedure.s GetFieldName(*This.Private_Members, FieldID.i)
    
    tmp = GetCurrentRecord(*This)
    SetCurrentRecord(*This, 0)
    ttt.s = Private_Rid(*This, FieldID)
    SetCurrentRecord(*This, tmp)
    
    ProcedureReturn ttt 
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Setters (Extra) <<<<<
  
  Procedure SetFieldName(*This.Private_Members, FieldID.i, FieldName.s) ;  Set a field name
    
    If FieldName <> ""
      tmp = GetCurrentRecord(*This)
      SetCurrentRecord(*This, 0)
      WriteRecord(*This, FieldID, FieldName)
      SetCurrentRecord(*This, tmp)
    EndIf
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The AddField operator <<<<<
  
  Procedure AddField(*This.Private_Members, FieldName.s) ;  Add a Field as the last field
    
    tmp = GetCurrentRecord(*This)
    *This\Tables()\FieldCount + 1
    
    ForEach *This\Tables()\Bdd()
      *This\Tables()\Bdd() + #Separator
    Next
    
    SetFieldName(*This, *This\Tables()\FieldCount, FieldName)
    SetCurrentRecord(*This, tmp)
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The DeleteField operator <<<<<
  
  Procedure DeleteField(*This.Private_Members, FieldID.i) ; Delete a field in the database
    
    tmp = GetCurrentRecord(*This)
    
    ForEach *This\Tables()\Bdd()
      
      ttmp.s = ""
      
      For n = 1 To *This\Tables()\FieldCount
        
        If FieldID <> n  
          ttmp + StringField(*This\Tables()\Bdd(), n, #Separator) + #Separator
        EndIf
        
      Next
      
      *This\Tables()\Bdd() = ttmp
      
    Next
    
    CountFields(*This)
    SetCurrentRecord(*This, tmp)
    
  EndProcedure  
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The SearchInit operator <<<<<
  
  Procedure SearchInit(*This.Private_Members, FieldID.i, SearchText.s, Option.i)
    
    ;  Search with options ( #CaseInsensitive #EveryWhere )
    ;  Initialise a search in LittleDataBase
    
    *This\Tables()\SearchPointer = 1
    *This\Tables()\SearchString = SearchText
    *This\Tables()\SearchOption = Option
    *This\Tables()\SearchField = FieldID
    *This\Tables()\SearchStop = 0
    *This\Tables()\SearchReturn = 0
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Search operator <<<<<
  
  Procedure.i Search(*This.Private_Members)
    
  ;  Search and return record that match ( 0 = There is no more record that match  )
  ;  You must stop call this function
    
    ; If there is no records : Quit
    If *This\Tables()\SearchPointer = 1 And CountRecords(*This) = 0
      *This\Tables()\SearchStop = 1
      *This\Tables()\SearchReturn = 0
    EndIf
    
    *This\Tables()\SearchReturn = 0
    
    If *This\Tables()\SearchPointer > CountRecords(*This) 
      *This\Tables()\SearchStop = 1 
    EndIf
    
    If *This\Tables()\SearchStop = 0
      
      For n = *This\Tables()\SearchPointer To CountRecords(*This)
        
        SetCurrentRecord(*This, n)
        
        xxc.s = ReadRecord(*This, *This\Tables()\SearchField)
        xxs.s = *This\Tables()\SearchString
        
        ; Search case Insensitive : Put all stirng to Upper Case
        If *This\Tables()\SearchOption & #CASE_INSENSITIVE
          xxc = UCase(xxc)
          xxs = UCase(xxs)
        EndIf
        
        ; Flag Stop : Quit at the next loop
        If n = CountRecords(*This) 
          *This\Tables()\SearchStop = 1 
        EndIf
        
        If *This\Tables()\SearchOption & #EVERYWHERE
          
          If FindString(xxc, xxs, 1) > 0 
            *This\Tables()\SearchPointer = n + 1
            *This\Tables()\SearchReturn = *This\Tables()\SearchPointer - 1
            Break 
          EndIf
          
        Else
          
          If xxc = xxs 
            *This\Tables()\SearchPointer = n + 1 
            *This\Tables()\SearchReturn = *This\Tables()\SearchPointer - 1
            Break 
          EndIf
          
        EndIf
        
      Next
      
    EndIf
    
    ProcedureReturn *This\Tables()\SearchReturn
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The SortNumerical operator <<<<<
  
  Procedure SortNumerical(*This.Private_Members, FieldID.i) ;  Sort the database assuming field is Numerical
    
    If IsRegularExpression(*This\RegexIsNumeric)
      
      Nb = CountRecords(*This)
      
      If Nb > 1 ; Si au moins 2 enregistrements
        
        PointerTemp = GetCurrentRecord(*This)
        
        If MatchRegularExpression(*This\RegexIsNumeric, ReadRecord(*This, FieldID))
          
          For n = 1 To Nb-1
            
            For i = n+1 To Nb
              
              SetCurrentRecord(*This, n)
              FirstRecord.f = ValF(ReadRecord(*This, FieldID))
              
              SetCurrentRecord(*This, i)
              SecondRecord.f = ValF(ReadRecord(*This, FieldID))
              
              If FirstRecord.f > SecondRecord.f
                
                SelectElement(*This\Tables()\Bdd(), n)
                *Temp00 = @*This\Tables()\Bdd()
                SelectElement(*This\Tables()\Bdd(), i)
                *Temp01 = @*This\Tables()\Bdd()
                SwapElements(*This\Tables()\Bdd(), *Temp00, *Temp01)
                
              EndIf
              
            Next i
            
          Next n
          
        EndIf
        
      EndIf
      
      SetCurrentRecord(*This, PointerTemp)
     
    EndIf
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The SortString operator <<<<<
  
  Procedure SortString(*This.Private_Members, FieldID.i, Option.i) ;  Sort the database assuming field is a String
        
    ;  Défault = CaseSensitive / Just 1 Option = #CaseInsensitive
  
    Nb = CountRecords(*This)
    
    If Nb > 1 ; There is at list 2 records
      
      PointerTemp = GetCurrentRecord(*This)
      
      For n = 1 To Nb-1
        
        For i = n + 1 To Nb
          
          SetCurrentRecord(*This, n)
          FirstRecord.s = ReadRecord(*This, FieldID)
          
          SetCurrentRecord(*This, i)
          SecondRecord.s = ReadRecord(*This, FieldID)
          
          ; Put all in UpperCase if #CASE_INSENSITIVE
          
          If Option = #CASE_INSENSITIVE
            FirstRecord = UCase(FirstRecord)
            SecondRecord = UCase(SecondRecord)
          EndIf
          
          If FirstRecord > SecondRecord
            SelectElement(*This\Tables()\Bdd(), n)
            *Temp00 = @*This\Tables()\Bdd()
            SelectElement(*This\Tables()\Bdd(), i)
            *Temp01 = @*This\Tables()\Bdd()
            SwapElements(*This\Tables()\Bdd(), *Temp00, *Temp01)
          EndIf
          
        Next i
        
      Next n
      
      SetCurrentRecord(*This, PointerTemp)
      
    EndIf  
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The FormatFields operator <<<<<
  
  Procedure.s FormatFields(*This.Private_Members, Separator.s)
    
    For FieldID = 1 To *This\Tables()\FieldCount
      
      Output.s + ReadRecord(*This, FieldID)
      
      If FieldID < *This\Tables()\FieldCount
        Output + Separator
      EndIf
      
    Next
    
    ProcedureReturn Output
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Save on file operator <<<<<
  
  Procedure.i Save(*This.Private_Members)
    
    TableTemp = GetCurrentTable(*This)
    PointerTemp = GetCurrentRecord(*This)
    
    FileID = CreateFile(#PB_Any, *This\FileName)
    
    If IsFile(FileID)
      
      ForEach *This\Tables()
        
        WriteStringN(FileID, "LittleTable:" + *This\Tables()\Name)
        
        ForEach *This\Tables()\Bdd()
          WriteStringN(FileID, *This\Tables()\Bdd())
        Next
        
      Next
      
      CloseFile(FileID)
      
      SetCurrentTable(*This, TableTemp)
      SetCurrentRecord(*This, PointerTemp)
      
      ProcedureReturn 1
      
    EndIf
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Open on file operator <<<<<
  
  Procedure.i Open(*This.Private_Members) ;  Open an existing Database ( Return 1 if success / 0 if fail ) Thanks to Baldrick
    
    FileID = ReadFile(#PB_Any, *This\FileName)
    
    If IsFile(FileID)
      
      Output = 1
      
      While Eof(FileID) = 0
        
        DataBaseLine.s = ReadString(FileID)
        
        If FindString(DataBaseLine, "LittleTable:")
          AddElement(*This\Tables())
          *This\Tables()\Name = StringField(DataBaseLine, 2, ":")
        Else
          AddElement(*This\Tables()\Bdd())
          *This\Tables()\Bdd() = DataBaseLine
        EndIf
        
      Wend
      
      CloseFile(FileID)
      
      ForEach *This\Tables()
        CountFields(*This)
      Next
      
    EndIf
    
    ProcedureReturn Output
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Close operator <<<<<
  
  Procedure Close(*This.Private_Members)
    
    ForEach *This\Tables()
      ClearList(*This\Tables()\Bdd())
      ClearList(*This\Tables()\Field())
    Next
    
    ClearList(*This\Tables())
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Destructor <<<<<

  Procedure Free(*This.Private_Members)
    
    If IsRegularExpression(*This\RegexIsNumeric)
      FreeRegularExpression(*This\RegexIsNumeric)
    EndIf
    
    FreeStructure(*This)
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Constructor <<<<<

  Procedure.i New(FileName.s)
    
    *This.Private_Members = AllocateStructure(Private_Members)
    *This\VirtualTable = ?START_METHODS
    
    *This\FileName = FileName
    *This\RegexIsNumeric = CreateRegularExpression(#PB_Any, "^[[:digit:].]+$")
    
    ProcedureReturn *This
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Virtual table entries <<<<<

  DataSection
    START_METHODS:
    Data.i @GetFileName()
    Data.i @GetCurrentTable()
    Data.i @GetCurrentRecord()
    Data.i @GetTableName()
    Data.i @GetFieldName()
    Data.i @SetFileName()
    Data.i @SetCurrentTable()
    Data.i @SetCurrentRecord()
    Data.i @SetTableName()
    Data.i @SetFieldName()
    Data.i @AddField()
    Data.i @DeleteField()
    Data.i @CreateTable()
    Data.i @SelectTable()
    Data.i @FirstTable()
    Data.i @PreviousTable()
    Data.i @NextTable()
    Data.i @CountTables()
    Data.i @DeleteTable()
    Data.i @CountFields()
    Data.i @CountRecords()
    Data.i @InsertRecord()
    Data.i @DeleteRecord()
    Data.i @FirstRecord()
    Data.i @PreviousRecord()
    Data.i @NextRecord()
    Data.i @ReadRecord()
    Data.i @WriteRecord()
    Data.i @SearchInit()
    Data.i @Search()
    Data.i @SortNumerical() 
    Data.i @SortString()
    Data.i @FormatFields()
    Data.i @Save()
    Data.i @Open()
    Data.i @Close()
    Data.i @Free()
    END_METHODS:
  EndDataSection
  
EndModule

CompilerIf #PB_Compiler_IsMainFile
  
  F1Racing.LittleDataBase::LittleDataBase = LittleDataBase::New("F1 Racing.ldb")
  
  F1Racing\CreateTable("Drivers", "Birth Date,Name,Surname")
  
  ; Add one Record
  F1Racing\InsertRecord(-1)
  ; Write data to this record
  F1Racing\WriteRecord(1, "1969") ; 1st field
  F1Racing\WriteRecord(2, "Schumacher") ; 2nd field
  F1Racing\WriteRecord(3, "Michael")     ; 3rd field
  
  ; Add another Record
  F1Racing\InsertRecord(-1)
  ; Write data to this record
  F1Racing\WriteRecord(1, "1980") ; 1st field
  F1Racing\WriteRecord(2, "Button") ; 2nd field
  F1Racing\WriteRecord(3, "Jenson") ; 3rd field
  
  ; Add another Record
  F1Racing\InsertRecord(-1)
  ; Write data to this record
  F1Racing\WriteRecord(1, "1981") ; 1st field
  F1Racing\WriteRecord(2, "Alonso") ; 2nd field
  F1Racing\WriteRecord(3, "Fernando") ; 3rd field
  
  ; Add another Record
  F1Racing\InsertRecord(-1)
  ; Write data to this record
  F1Racing\WriteRecord(1, "1971") ; 1st field
  F1Racing\WriteRecord(2, "Villeneuve") ; 2nd field
  F1Racing\WriteRecord(3, "Jacques")    ; 3rd field
  
  ; Insert a record at 3rd position
  F1Racing\InsertRecord(3)
  ; Write data to this record
  F1Racing\WriteRecord(1, "1975") ; 1st field
  F1Racing\WriteRecord(2, "Schumacher") ; 2nd field
  F1Racing\WriteRecord(3, "Ralph")      ; 3rd field
  
  F1Racing\CreateTable("Cars", "Frame,Engine")
  
  F1Racing\InsertRecord(-1)
  F1Racing\WriteRecord(1, "Ferrari") ; 1st field
  F1Racing\WriteRecord(2, "Ferrari") ; 2nd field
  
  F1Racing\InsertRecord(-1)
  F1Racing\WriteRecord(1, "Williams") ; 1st field
  F1Racing\WriteRecord(2, "Renault") ; 2nd field
  
  F1Racing\InsertRecord(-1)
  F1Racing\WriteRecord(1, "Jordan") ; 1st field
  F1Racing\WriteRecord(2, "Peugot") ; 2nd field
  
  ; now we return to the Drivers table
  F1Racing\SelectTable(-1, "Drivers")
  
  Debug "Drivers unsorted"
  
  For n=1 To F1Racing\CountRecords()
    F1Racing\SetCurrentRecord(n)
    Debug F1Racing\FormatFields(" ")
  Next
  
  Debug ""
  
  ; Sort the database by field 1 ( Birth Date )
  F1Racing\SortNumerical(1)
  
  ; Show all drivers sorted by birth Date
  Debug "Drivers sorted by birth date"
  
  For n=1 To F1Racing\CountRecords()
    F1Racing\SetCurrentRecord(n)
    Debug F1Racing\FormatFields(" ")
  Next
  
  Debug ""
  
  ; Sort the database by Drivers names
  F1Racing\SortString(2, LittleDataBase::#CASE_INSENSITIVE)
  
  ; Show all drivers sorted by name
  Debug "Drivers sorted by name"
  
  For n=1 To F1Racing\CountRecords()
    F1Racing\SetCurrentRecord(n)
    Debug F1Racing\FormatFields(" ")
  Next
  
  Debug ""
  
  ; Search all name = Schumacher
  F1Racing\SearchInit(2, "Schumacher", LittleDataBase::#CASE_INSENSITIVE)
  
  ; Show all drivers = Schumacher
  Debug "Drivers with name = Schumacher"
  
  Champ = F1Racing\Search()
  
  While Champ <> 0
    
    F1Racing\SetCurrentRecord(Champ)
    Debug F1Racing\FormatFields(" ")
    Champ = F1Racing\Search()
    
  Wend
  
  Debug ""
  
  ; Database Infos
  Debug "Database Infos"
  
  Debug "Number of Tables " + Str(F1Racing\CountTables())
  
  F1Racing\FirstTable()
  
  For TableID = 1 To F1Racing\CountTables()
    
    Debug "Table n°" + Str(TableID) + " = " + F1Racing\GetTableName()
    Debug "Number of fields " + Str(F1Racing\CountFields())
    Debug "Name of field"
    
    For n = 1 To F1Racing\CountFields()
      Debug "Field n°" + Str(n) + " = " + F1Racing\GetFieldName(n)
    Next
    
    Debug "Number of records " + Str(F1Racing\CountRecords())
    Debug ""
    
    F1Racing\NextTable()
    
  Next
  
  Debug "Database Content"
  Debug ""
  
  F1Racing\FirstTable()
  
  For TableID = 1 To F1Racing\CountTables()
    
    Debug "Table n°" + Str(TableID) + " = " + F1Racing\GetTableName()
    
    F1Racing\FirstRecord()
    
    For RecordID = 1 To F1Racing\CountRecords()
      Debug F1Racing\FormatFields(" ")
      F1Racing\NextRecord()
    Next
    
    F1Racing\NextTable()
    Debug ""
    
  Next
  
  ; Save Database to disk
  F1Racing\Save()
  F1Racing\Close()
  
  Debug "--------------------------------------------------------"
  Debug "Loading from file test"
  Debug ""
  
  F1Racing\Open()
  
  Debug "Database Content"
  Debug ""
  
  F1Racing\FirstTable()
  
  For TableID = 1 To F1Racing\CountTables()
    
    Debug "Table n°" + Str(TableID) + " = " + F1Racing\GetTableName()
    
    F1Racing\FirstRecord()
    
    For RecordID = 1 To F1Racing\CountRecords()
      Debug F1Racing\FormatFields(" ")
      F1Racing\NextRecord()
    Next
    
    F1Racing\NextTable()
    Debug ""
    
  Next
  
  F1Racing\Free()
  
  If FileSize("F1 Racing.ldb") > 0
    DeleteFile("F1 Racing.ldb")
  EndIf
  
CompilerEndIf

; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<
The Stone Age did not end due to a shortage of stones !
Post Reply