Author Topic: ISAM - a multi-platform indexed database  (Read 1732 times)

Offline backspace

  • Mr. Drawsprite
  • **
  • Posts: 53
    • View Profile
I'm really getting the hang of GLBasic now. It's time to give back for all the help I've received on the forum.

ISAM.gbal is included in the attached zip file, together with all sources.

ISAM is a GLBasic library that uses a UNIX concept of an indexed database.
ISAM stands for Indexed Sequential Access Method. A way of storing indexed data in a text file.

The library provides a TYPE named _ISAM, which can be used to manage simple databases on all platforms
The database is used in memory, and it can be saved to a textfile, and later loaded on demand

You can use as many databases at the same time as you wish.
You declare a database by using the _ISAM TYPE

friends AS _ISAM -is all that is needed to initialize the database
You can have as many rows and columns in the database as you wish.
You don't waste memory because row/column fields are only created when you "set" them.
All data is saved as strings, so you will need to convert the data if you wish to use them as numbers.

The ISAM library source
Code: GLBasic [Select]
// ------------------------------------------------------- //
// Project: ISAM
// Indexed Sequential Access Method Database file Manager
// ------------------------------------------------------ //


TYPE _ISAMINFO
        index$
        field$[]
        info$[]
ENDTYPE

TYPE _ISAM
        info[] AS _ISAMINFO

        //! set: This function sets a variable to the database.
        //! The index is used as a key to a table row
        //! Each table row may have multiple fields, but must use the same index to add the field to the row
        //! Each entry is only stored as a string. You will need to convert it in your code, if you wish to use numbers
        //! If a field already exists, the data in that field will be updated.
        //!
        //! Parameters:
        //! index$: The index for the row in the database
        //! field$: The name of the column field in the index row of the database
        //! info$ : The info you wish to store in the database field$ in the index$ row
        //!
        //! Returns: Nothing
        //!
        //! Example: set("JoeH", "Name", "Joseph"); set("JoeH","Surname","Higgins")
        FUNCTION set: index$, field$, info$
                LOCAL i,L,j,found
                L=LEN(self.info[])
                found = FALSE
                FOR i = 0 TO LEN(self.info[])-1
                        IF index$ = self.info[i].index$
                                //first look for item to replace
                                FOR j = 0 TO LEN(self.info[i].field$[])-1
                                        IF self.info[i].field$[j] = field$
                                                self.info[i].info$[j] = info$
                                                RETURN //change made - skip rest of function
                                        ENDIF
                                NEXT
                                DIMPUSH self.info[i].field$[], field$
                                DIMPUSH self.info[i].info$[],info$
                                found=TRUE
                                BREAK
                        ENDIF
                NEXT
                IF NOT found
                        L=BOUNDS(self.info[], 0)
                        REDIM self.info[L+1]
                        self.info[L].index$ = index$
                        DIMPUSH self.info[L].field$[], field$
                        DIMPUSH self.info[L].info$[],info$
                ENDIF
        ENDFUNCTION

        //! get: This function gets a variable from the database.
        //! The index is used as a key to a table row
        //! The index and the field must exist in the database, else the function returns a blank string
        //!
        //! Parameters:
        //! index$: The index for the row in the database
        //! field$: The name of the column field in the index row of the database
        //!
        //! Returns: On success, rerurns the info from the field. On failure, returns a blank string
        //!
        //! Example: name$ = get("JoeH", "Name")
        FUNCTION get$: index$, field$
                LOCAL i,j,L
                LOCAL info$
                FOR i = 0 TO LEN(self.info[])-1
                        IF index$ = self.info[i].index$
                                FOR j = 0 TO LEN(self.info[i].field$[])-1
                                        IF self.info[i].field$[j] = field$
                                                info$ = self.info[i].info$[j]
                                                RETURN info$
                                        ENDIF
                                NEXT
                        ENDIF
                NEXT
                RETURN ""
        ENDFUNCTION

        //! indexcount: This function returns the number of rows in the database.
        //!
        //! Parameters:
        //! None
        //!
        //! Returns: The number of rows in the database
        //!
        //! Example: rows% = indexcount()
        FUNCTION indexcount:
                RETURN LEN(self.info[])
        ENDFUNCTION

        //! getindex$: This function gets the index string for the specified database row
        //!
        //! Parameters:
        //! num%:  The number of the database row to access
        //!
        //! Returns: The index string for the specified database row
        //!
        //! Example: index$ = getindex$(2)
        FUNCTION getindex$: num%
                IF LEN(self.info[]) < 1 THEN RETURN
                RETURN self.info[num].index$
        ENDFUNCTION

        //! fieldcount: This function gets the number of fields in an indexed database row
        //!
        //! Parameters:
        //! index$: The index name of the database row to query
        //!
        //! Returns: The number of fields in the indexed row, or 0 if there are no fields
        //!
        //! Example: numFields = fieldcount("JoeH")
        FUNCTION fieldcount: index$
                LOCAL i
                FOR i = 0 TO LEN(self.info[])-1
                        IF self.info[i].index$ = index$
                                RETURN LEN(self.info[i].field$[])
                        ENDIF
                NEXT
                RETURN 0
        ENDFUNCTION

        //! getfield$: This function gets the name of the specified field column number in the indexed row
        //!
        //! Parameters:
        //! index$: The index name for the database row
        //! num%:   The number of the field column in the row
        //!
        //! Returns: The name of the field column, or "" if the field does not exist
        //!
        //! Example: filed/name$ = getfield$("JoeH", 1)
                FUNCTION getfield$: index$, num%
                LOCAL i,j,L
                FOR i = 0 TO LEN(self.info[])-1
                        IF self.info[i].index$ = index$
                                RETURN self.info[i].field$[num]
                        ENDIF
                NEXT
                RETURN ""
        ENDFUNCTION

        //! delindex: This function deletes an entire indexed row from the database
        //! Note: This action is not reversable.
        //!
        //! Parameters:
        //! index$: The index name of the database row ro delete
        //!
        //! Returns: 1 on success, -1 on failure
        //!
        //! Example: delindex("JoeH")
        FUNCTION delindex: index$
                LOCAL i,L
                FOR i = 0 TO LEN(self.info[])-1
                        IF self.info[i].index$ = index$
                                DIMDEL self.info[],i
                                RETURN 1
                        ENDIF
                NEXT
                RETURN -1
        ENDFUNCTION

        //! save: This function saves the entire database as a textfile to disk
        //!
        //! Parameters:
        //! filename$: The file name to which to save the database
        //!
        //! Returns: 1 on success, -1 on failure
        //!
        //! Example: save("myfriends.db")
        FUNCTION save: filename$
                LOCAL ok = OPENFILE(1, filename$,0)
                LOCAL i,j,s$
                IF NOT ok THEN RETURN -1
                s$ = LEN(self.info[])
                WRITESTR 1, s$+"\n"
                FOR i = 0 TO LEN(self.info[])-1
                        WRITESTR 1, self.info[i].index$ + "\n"
                        s$ = LEN(self.info[i].field$[])
                        WRITESTR 1,s$ + "\n"
                        FOR j = 0 TO LEN(self.info[i].field$[])-1
                                WRITESTR 1, self.info[i].field$[j]+"\n"
                                WRITESTR 1, self.info[i].info$[j]+"\n"
                        NEXT
                NEXT
                CLOSEFILE 1
                RETURN 1
        ENDFUNCTION


        //! load: This function loads a previously saved database back into memory
        //!
        //! Parameters:
        //! filename$: The file name from which to read the database
        //!
        //! Returns: 1 on success, -1 on failure
        //!
        //! Example: load("myfriends.db")
        FUNCTION load: filename$
                REDIM self.info[0]
                IF NOT DOESFILEEXIST(filename$) THEN RETURN -1
                LOCAL ok = OPENFILE(1, filename$,1)
                IF NOT ok THEN RETURN -1
                LOCAL xi,xj,xIdxcount%,xfieldcnt%, s$
                LOCAL xindex$,xfield$,xinfo$
                IF NOT ok THEN RETURN
                READLINE 1,s$
                xIdxcount =  s$
                FOR xi = 0 TO xIdxcount-1
                        READLINE 1, xindex$
                        READLINE 1, s$
                        xfieldcnt = s$
                        FOR xj = 0 TO xfieldcnt -1
                                READLINE 1,xfield$
                                READLINE 1,xinfo$
                                self.set(xindex$,xfield$,xinfo$)
                        NEXT
                NEXT
                CLOSEFILE 1
                RETURN 1
        ENDFUNCTION
ENDTYPE

//! ISAM is a GLBasic library that uses a UNIX concept of an indexed database.
//! ISAM stands for Indexed Sequential Access Method. A way of storing indexed data in a text file.
//!
//! The library provides a TYPE named _ISAM, which can be used to manage simple databases on all platforms
//! The database is used in memory, and it can be saved to a textfile, and later loaded on demand
//!
//! You can use as many databases at the same time as you wish.
//! You declare a database by using the _ISAM TYPE
//!
//! friends AS _ISAM    -is all that is needed to initialize the database
//! You can have as many rows and columns in the database as you wish.
//! You don't waste memory because row/column fields are only created when you "set" them.
//! All data is saved as strings, so you will need to convert the data if you wish to use them as numbers.
//!
//!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//! An example of using the database
//!~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//!
//! LOCAL friends AS _ISAM
//! LOCAL info$
//! LOCAL i
//! LOCAL field$
//! LOCAL index$
//!
//! friends.set("JoeH","Name","Joe")
//! friends.set("JoeH","Surname","Higgins")
//! friends.set("JoeH","Phone","123-4567")
//! friends.set("JoeH","Birthday","12-12-90")
//! friends.set("JoeH","Email","joeh@somewhere.earth")
//!
//! friends.set("AnneP","Name","Anne")
//! friends.set("AnneP","Surname","Pigtails")
//! friends.set("AnneP","Phone","765-4321")
//! friends.set("AnneP","Birthday","11-11-91")
//! friends.set("AnneP","Email","annepig@somewhere.moon")
//!
//! friends.set("AlbertE","Name","Albert")
//! friends.set("AlbertE","Surname","Einstein")
//! friends.set("AlbertE","Phone","755-1212")
//! friends.set("AlbertE","Birthday","01-01-1901")
//! friends.set("AlbertE","Email","albert.einstein@relativity.e=mc2")
//!
//! friends.set("SrIsac","Name","Isac")
//! friends.set("SrIsac","Surname","Newton")
//! friends.set("SrIsac","Phone","001-00706")
//! friends.set("SrIsac","Birthday","11-11-61")
//! friends.set("SrIsac","Email","isac.apple@under.tree")
//! friends.set("SrIsac","Birthday","03-03-63") //change
//!
//! DEBUG friends.indexcount()
//!
//! FOR i = 0 TO friends.indexcount()-1
//!     DEBUG friends.getindex$(i)+"\n"
//! NEXT
//!
//! friends.delindex("AlbertE")
//!
//! index$ = friends.getindex$(2)
//!
//! DEBUG "\n"
//!
//! FOR i = 0 TO friends.fieldcount(index$)-1
//!     DEBUG friends.getfield$(index$, i)+"\n"
//! NEXT
//!
//! DEBUG "\n"
//!
//! FOR i = 0 TO friends.fieldcount(index$)-1
//!     field$ = friends.getfield$(index$, i)
//!     DEBUG friends.get$(index$,field$)+"\n"
//! NEXT
//!
//! friends.save("testfile")
//!
//! friends.load("testfile")
FUNCTION __help__:
ENDFUNCTION
 

The ISAM test program
Code: GLBasic [Select]
// --------------------------------- //
// Project: ISAM - Testfile
// --------------------------------- //

LOCAL friends AS _ISAM
LOCAL info$
LOCAL i
LOCAL field$
LOCAL index$

friends.set("JoeH","Name","Joe")
friends.set("JoeH","Surname","Higgins")
friends.set("JoeH","Phone","123-4567")
friends.set("JoeH","Birthday","12-12-90")
friends.set("JoeH","Email","joeh@somewhere.earth")

friends.set("AnneP","Name","Anne")
friends.set("AnneP","Surname","Pigtails")
friends.set("AnneP","Phone","765-4321")
friends.set("AnneP","Birthday","11-11-91")
friends.set("AnneP","Email","annepig@somewhere.moon")

friends.set("AlbertE","Name","Albert")
friends.set("AlbertE","Surname","Einstein")
friends.set("AlbertE","Phone","755-1212")
friends.set("AlbertE","Birthday","01-01-1901")
friends.set("AlbertE","Email","albert.einstein@relativity.e=mc2")

friends.set("SrIsac","Name","Isac")
friends.set("SrIsac","Surname","Newton")
friends.set("SrIsac","Phone","001-00706")
friends.set("SrIsac","Birthday","11-11-61")
friends.set("SrIsac","Email","isac.apple@under.tree")
friends.set("SrIsac","Birthday","03-03-63") //change

DEBUG friends.indexcount()

FOR i = 0 TO friends.indexcount()-1
        DEBUG friends.getindex$(i)+"\n"
NEXT

friends.delindex("AlbertE")

index$ = friends.getindex$(2)

DEBUG "\n"

FOR i = 0 TO friends.fieldcount(index$)-1
        DEBUG friends.getfield$(index$, i)+"\n"
NEXT

DEBUG "\n"

FOR i = 0 TO friends.fieldcount(index$)-1
        field$ = friends.getfield$(index$, i)
        DEBUG friends.get$(index$,field$)+"\n"
NEXT

friends.save("testfile")

friends.load("testfile")
 
I came, I saw, I coded.

Offline kanonet

  • Administrator
  • Prof. Inline
  • *******
  • Posts: 1142
    • View Profile
    • My GLBasic code archiv
Re: ISAM - a multi-platform indexed database
« Reply #1 on: 2013-Jan-28 »
Did not have a deep look into your code yet, but i think this can be useful, ty.

Just one note, cause i see this very often:

Instead of writing this:
Code: GLBasic [Select]
LOCAL i
FOR i=0 to 5
NEXT
better write this:
Code: GLBasic [Select]
FOR i%=0 to 5
NEXT
-no need for LOCAL i, its just not necessary, cause FOR defines i anyway
-integers are faster than floats, so i% is better here
Lenovo Thinkpad T430u: Intel i5-3317U, 8GB DDR3, NVidia GeForce 620M, Micron RealSSD C400 @Win7 x64

Offline Marmor

  • Community Developer
  • Prof. Inline
  • ******
  • Posts: 908
  • 96A285CC
    • View Profile
    • my youtube channel
Re: ISAM - a multi-platform indexed database
« Reply #2 on: 2013-Jan-28 »
Remember me for powerisam ( isam for powerbasic) !
Its great !! Thx