ISAM - a multi-platform indexed database

Previous topic - Next topic

backspace

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")



[attachment deleted by admin]
I came, I saw, I coded.

kanonet

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

Marmor

Remember me for powerisam ( isam for powerbasic) !
Its great !! Thx