SQLite Wrapper Functions

Share your advanced PureBasic knowledge/code with the community.
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

SQLite Wrapper Functions

Post by BackupUser »

Code updated For 5.20+ (same As UseSQLiteDatabase())

Restored from previous forum. Originally posted by ebs.

Well, after a day of work, here are PB wrapper functions to use with the SQLite database DLL (http://www.hwaci.com/sw/sqlite/).

Since the Windows DLL uses the CDECL calling convention, I had to pop the calling parameters off the stack manually. Make sure that you have "Enable Inline ASM Support" checked. For speed reasons, I wanted to use CallFunctionFast() instead of CallFunction(), but I couldn't make it work.

I also attached the SQLite DLL to the PB executable and extracted it at run-time. This isn't absolutely necessary, but I like the idea of having everything in one executable, with no external DLLs.

Fred: I continue to be amazed that you have created such an incredible product! It's hard to believe that I can provide an entire SQL database application in one executable under 200KB, even less if you compress it! That's *not* to downplay the work of D. Richard Hipp, who created SQLite, an equally amazing piece of work. Considering that accomplishing the same thing with Visual Basic/ODBC/Access/SQL Server requires hundreds of megabytes, I think this is GREAT!

Let's hear it for all the folks using PB for boring old database programming!

Code: Select all

#True = 1
#False = 0

#SQLiteLib = 1
#SQLITE_OK = 0

Declare.l SQLite_Init()
Declare.l SQLite_Open(Filename.s, Mode.l)
Declare.l SQLite_Get_Table(DB_Handle.l, SQLString.s, *Rows, *Cols)
Declare.l SQLite_Exec(DB_Handle.l, SQLString.s)
Declare   SQLite_Close(DB_Handle.l)
Declare.s SQLite_LibVersion()

; data returned from database query
Dim DBData.s(1,1)
; number of rows/columns returned
; NB: row 0 is column headers
Rows.l
Cols.l
; database operation result
Result.l

; extract SQLite DLL and write to disk
If OpenFile(1, "PBsqlite.dll")
  WriteData(1,?SQLite, 196096)
  CloseFile(1)
Else
  MessageRequester("SQLite Error", "Can't write DLL file", #MB_ICONERROR | #MB_OK)
  End
EndIf

; make sure SQLite library is present
If SQLite_Init()
  ; display SQLite version
  Debug "SQLite version " + SQLite_LibVersion()
  Debug ""
  ; open database
  DBName.s = "test.db"
  DBHandle.l = SQLite_Open(DBName, 0)
  If DBHandle
    ; insert new row into database
    SQL.s = "INSERT INTO bookmarks VALUES(789,'http://junk','junk','djunk')"
    Result = SQLite_Exec(DBHandle, SQL)
    Debug "INSERT result = " + Str(Result)
    Debug ""
    ; execute SQL query
    SQL.s = "SELECT id, url, site, category FROM bookmarks WHERE category LIKE 'd%'"
    Result = SQLite_Get_Table(DBHandle, SQL, @Rows, @Cols)
    If Result = #SQLITE_OK
      ; get the results
      ; display number of rows/columns
      Debug "Rows = " + Str(Rows)
      Debug ""
      Debug "Columns = " + Str(Cols)
      ; display column headers
      For Col.l = 0 To Cols-1
        Debug DBData(0, Col)
      Next       
      ; display returned rows
      For Row.l = 1 To Rows
        Debug ""
        Debug "Data row " + Str(Row) + " :"
        For Col.l = 0 To Cols-1
          Debug DBData(Row, Col)
        Next
      Next
    Else
      MessageRequester("SQLite Error", "Get_Table error = " + Str(Result), #MB_ICONERROR | #MB_OK)
    EndIf
  Else
    MessageRequester("SQLite Error", "Can't open database " + DBName, #MB_ICONERROR | #MB_OK)
  EndIf
Else
  MessageRequester("SQLite Error", "Can't open sqlite.dll", #MB_ICONERROR | #MB_OK)
EndIf

; remember to close database
SQLite_Close(DBHandle)
; and delete SQLite DLL
CloseLibrary(#SQLiteLib)
DeleteFile("PBsqlite.dll")
End

; *** SQLite Wrapper Functions ***

; make sure SQLite DLL is present
Procedure.l SQLite_Init()
  If OpenLibrary(#SQLiteLib, "PBsqlite.dll")
    ProcedureReturn #True 
  Else
    ProcedureReturn #False
  EndIf
EndProcedure

; open specified database
; NB: Mode is currently not used
Procedure.l SQLite_Open(Filename.s, Mode.l)
  Shared Result.l
  
  Result = CallFunction(#SQLiteLib, "sqlite_open", @Filename, Mode, 0)
  !ADD esp,12
  
  ProcedureReturn Result
EndProcedure

; execute SQL query and return results
Procedure.l SQLite_Get_Table(DB_Handle.l, SQLString.s, *Rows, *Cols)
  Shared Result.l
  Shared LResultsPtr.l
  Shared LRows.l
  Shared LCols.l
  
  Result = CallFunction(#SQLiteLib, "sqlite_get_table", DB_Handle, @SQLString, @LResultsPtr, @LRows, @LCols, 0)
  !ADD esp,24
  
  If Result = #SQLITE_OK
    ; return number of rows/columns
    PokeL(*Rows, LRows)
    PokeL(*Cols, LCols)
    
    ; redimension results array
    Dim DBData.s(LRows, LCols-1)
    ; copy data into array
    Address.l = 0
    For row.l = 0 To LRows
      For col.l = 0 To LCols-1
        DBData(row, col) = PeekS(PeekL(LResultsPtr + Address + col * 4))
      Next
      Address + LCols * 4
    Next
    ; free table memory
    CallFunction(#SQLiteLib, "sqlite_free_table", LResultsPtr)
    !ADD esp,4
  EndIf
  
  ProcedureReturn Result
EndProcedure

; execute SQL statement (no results)
Procedure.l SQLite_Exec(DB_Handle.l, SQLString.s)
  Shared Result.l
  
  Result = CallFunction(#SQLiteLib, "sqlite_exec", DB_Handle, @SQLString, 0, 0, 0)
  !ADD esp,20
  
  ProcedureReturn Result
EndProcedure

; close database
Procedure SQLite_Close(DB_Handle.l)
  CallFunction(#SQLiteLib, "sql_close", DB_Handle)
  !ADD esp,4
EndProcedure

; get SQLite version
Procedure.s SQLite_LibVersion()
  ProcedureReturn PeekS(CallFunction(#SQLiteLib, "sqlite_libversion"))
EndProcedure

SQLite:
IncludeBinary "SQLite\sqlite.dll"

BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by fred.

Nice work ebs !
BTW, You can use
!ADD esp,4
to allow ASM without use the 'Enable ASM' switch. I definitely need to support C convention.


Fred - AlphaSND
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by Rings.

very nice work, congrats from me.

One Tip for including external Stuff:


; extract SQLite DLL and write to disk
If OpenFile(1, "PBsqlite.dll")
WriteData(?SQLite, ?SQLiteEnd-?SQLite) ;so you do not need the exact length :)
CloseFile(1)
Else
MessageRequester("SQLite Error", "Can't write DLL file", #MB_IconError | #MB_OK)
End
EndIf



..
..

SQLite:
IncludeBinary "SQLite\sqlite.dll"
SQLiteEnd:

Its a long way to the top if you wanna .....CodeGuru
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by Berikco.

Very nice
Thx for sharing

Regards,

Berikco

http://www.benny.zeb.be/purebasic.htm
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by Midebor.

Hi,
Thx You for sharing your code ebs. Released last night:

Version 2.7.6 of SQLite is now available on the website:

http://www.sqlite.org/

The focus of this release is performance improvements.
SQLite is now consistently faster than MySQL (its closest
rival in terms of speed). For UPDATEs, SQLite version 2.7.6
is generally more than twice as fast as prior versions.

This release also includes the new sqlite_set_authorizer()
API, though that API has not yet been documented.

Regards, Michel
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by ebs.

Fred, Rings, Berikco, and Midebor,

Thanks for the kind words, code improvements, and update notice.

Regards,
Eric
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by ebs.

Fred,

I'm tweaking my code to improve its speed. Is "col << 2" faster than "col * 4", or does the compiler make that substitution for me?

Eric
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by ricardo.

Hi ebs,

Which version of PBsqlite did you use to make the code, im using 2.7.6 and it fails with Gat_table error #1 when i try to open it.



Best Regards

Ricardo

Dont cry for me Argentina...
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by Midebor.

Hi Ricardo,

test.db is missing ...
You ccan create one with the sqlite.exe utility
1. sqlite test.db
2. CREATE TABLE bookmarks (id, url, site, category);
3. INSERT INTO bookmarks VALUES ('1', '[url]http://www.purebasic.com',[/url]
'Purebasic', 'basic');

Copy test.db in purebasic dir and run debug ...

Michel
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by fred.
Originally posted by ebs

Fred,

I'm tweaking my code to improve its speed. Is "col << 2" faster than "col * 4", or does the compiler make that substitution for me?
The compiler does it for you :).

Code: Select all

; a = a*4
  MOV    ebx,dword [v_a]
  SAL    ebx,2
  MOV    dword [v_a],ebx
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by Manolo.

I ebs,

Congratulations. Is very nice. I will work with youy code.

Regards.
Manolo
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by blueb.

Ricardo,
The program above fails with SQLite 2.7.6 because the sqlite.dll size has changed. Simply replace the code as suggested by Rings

--blueb
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by webba.

Hi all!

You guys are great! I am definately using the right programming language. Congrats to ebs for the effort put into this and the suggestions of all you other guys. I have been wanting something like this for a while now.

Brilliant!

Thanks again - I'll be using this in my current project!
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by ebs.

As promised (threatened?), here is the latest version of my SQLite Wrapper Functions. There is now support for more of the SQLite functions. I also incorporated Rings' suggestion, so it will work with any size DLL, and Fred's suggestion, so you don't need to enable Inline ASM.

In case I didn't mention it before, the "test.db" I'm using comes with the "EasySQLite" command line program at http://jansfreeware.com/, but you can easily make your own, as Midebor points out.

Sounds like there are a lot more "boring database programmers" out there than I thought!

Code: Select all

#True = 1
#False = 0

#SQLiteLib = 1
#SQLITE_OK = 0

Declare.l SQLite_Init()
Declare.l SQLite_Open(Filename.s, Mode.l)
Declare.l SQLite_Get_Table(DB_Handle.l, SQLString.s, *Rows, *Cols)
Declare.l SQLite_Exec(DB_Handle.l, SQLString.s)
Declare.l SQLite_Changes(DB_Handle.l)
Declare   SQLite_Interrupt(DB_Handle.l)
Declare.s SQLite_Error_String(ErrorCode.l)
Declare   SQLite_Close(DB_Handle.l)
Declare.s SQLite_LibVersion()

; data returned from database query
Dim DBData.s(1,1)
; number of rows/columns returned
; NB: row 0 is column headers
Rows.l
Cols.l
; database operation result
Result.l

; extract SQLite DLL and write to disk
If OpenFile(1, "PBsqlite.dll")
  WriteData(?SQLite, ?SQLiteEnd-?SQLite)
  CloseFile(1)
Else
  MessageRequester("SQLite Error", "Can't write DLL file", #MB_IconError | #MB_OK)
  End
EndIf

; make sure SQLite library is present
If SQLite_Init()
  ; display SQLite version
  Debug "SQLite version " + SQLite_LibVersion()
  Debug ""
  ; open database
  DBName.s = "test.db"
  DBHandle.l = SQLite_Open(DBName, 0)
  If DBHandle
    ; insert new row into database
    SQL.s = "INSERT INTO bookmarks VALUES(987,'http://junk2','junk2','djunk2')"
    Result = SQLite_Exec(DBHandle, SQL)
    Debug "INSERT result = " + Str(Result) + " - " + SQLite_Error_String(Result)
    Debug ""
    Debug "Rows changed = " + Str(SQLite_Changes(DBHandle))
    Debug ""
    ; execute SQL query
    SQL.s = "SELECT id, url, site, category FROM bookmarks WHERE category LIKE 'd%'"
    Result = SQLite_Get_Table(DBHandle, SQL, @Rows, @Cols)
    If Result = #SQLITE_OK
      ; get the results
      ; display number of rows/columns
      Debug "Rows = " + Str(Rows)
      Debug ""
      Debug "Columns = " + Str(Cols)
      ; display column headers
      For Col.l = 0 To Cols - 1
        Debug DBData(0, Col)
      Next        
      ; display returned rows
      For Row.l = 1 To Rows
        Debug ""
        Debug "Data row " + Str(Row) + " :"
        For Col = 0 To Cols - 1
          Debug DBData(Row, Col)
        Next
      Next
    Else
      MessageRequester("SQLite Error", "Get_Table error = " + Str(Result), #MB_IconError | #MB_OK)
    EndIf
  Else
    MessageRequester("SQLite Error", "Can't open database " + DBName, #MB_IconError | #MB_OK)
  EndIf
Else
  MessageRequester("SQLite Error", "Can't open sqlite.dll", #MB_IconError | #MB_OK)
EndIf

; remember to close database
SQLite_Close(DBHandle)
; and delete SQLite DLL
CloseLibrary(#SQLiteLib)
DeleteFile("PBsqlite.dll")
End

; *** SQLite Wrapper Functions ***

; make sure SQLite DLL is present
Procedure.l SQLite_Init()
  If OpenLibrary(#SQLiteLib, "PBsqlite.dll")
    ProcedureReturn #True  
  Else
    ProcedureReturn #False
  EndIf
EndProcedure

; open specified database
; NB: Mode is currently not used
Procedure.l SQLite_Open(Filename.s, Mode.l)
  Shared Result.l
  
  Result = CallFunction(#SQLiteLib, "sqlite_open", Filename, Mode, 0)
  !ADD esp,12
  
  ProcedureReturn Result
EndProcedure

; execute SQL query and return results
Procedure.l SQLite_Get_Table(DB_Handle.l, SQLString.s, *Rows, *Cols)
  Shared Result.l
  Shared LResultsPtr.l
  Shared LRows.l
  Shared LCols.l

  Result = CallFunction(#SQLiteLib, "sqlite_get_table", DB_Handle, SQLString, @LResultsPtr, @LRows, @LCols, 0)
  !ADD esp,24 

  If Result = #SQLITE_OK
    ; return number of rows/columns
    PokeL(*Rows, LRows)
    PokeL(*Cols, LCols)
 
    ; redimension results array (clears data)
    Dim DBData.s(LRows, LCols-1)
    ; copy data into array
    Address.l = LResultsPtr
    AddrInc.l = LCols * 4
    For row.l = 0 To LRows
      For col.l = 0 To LCols-1
        DBData(row, col) = PeekS(PeekL(Address + col * 4))
      Next
    Address + AddrInc
    Next
    ; free table memory
    CallFunction(#SQLiteLib, "sqlite_free_table", LResultsPtr)
    !ADD esp,4
  EndIf
  
  ProcedureReturn Result
EndProcedure

; execute SQL statement (no results)
Procedure.l SQLite_Exec(DB_Handle.l, SQLString.s)
  Shared Result.l

  Result = CallFunction(#SQLiteLib, "sqlite_exec", DB_Handle, SQLString, 0, 0, 0)
  !ADD esp,20
  
  ProcedureReturn Result
EndProcedure

; get number of rows changed by last operation
Procedure.l SQLite_Changes(DB_Handle.l)
  Shared RowsChanged.l
  
  RowsChanged = CallFunction(#SQLiteLib, "sqlite_changes", DB_Handle)
  !ADD esp,4
  
  ProcedureReturn RowsChanged
EndProcedure

; interrupt current operation
; NB: not used with "SQLite_Get_Table()"
Procedure SQLite_Interrupt(DB_Handle.l)
  CallFunction(#SQLiteLib, "sql_interrupt", DB_Handle)
  !ADD esp,4  
EndProcedure

; translate error code into string
Procedure.s SQLite_Error_String(ErrorCode.l)
  Shared ErrStrAddr.l
  
  ErrStrAddr = CallFunction(#SQLiteLib, "sqlite_error_string", ErrorCode)
  !ADD esp,4
  ProcedureReturn PeekS(ErrStrAddr)
EndProcedure

; close database
Procedure SQLite_Close(DB_Handle.l)
  CallFunction(#SQLiteLib, "sql_close", DB_Handle)
  !ADD esp,4
EndProcedure

; get SQLite version
Procedure.s SQLite_LibVersion()
  ProcedureReturn PeekS(CallFunction(#SQLiteLib, "sqlite_libversion"))
EndProcedure

SQLite:
IncludeBinary "SQLite\sqlite.dll"
SQLiteEnd:
Originally posted by webba

Hi all!

You guys are great! I am definately using the right programming language. Congrats to ebs for the effort put into this and the suggestions of all you other guys. I have been wanting something like this for a while now.

Brilliant!

Thanks again - I'll be using this in my current project!
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by El_Choni.

@ebs: did you take a look at the lib I sent you?

El_Choni
Post Reply