SQLite3 include
Verfasst: 02.11.2005 17:46
da ich während der benutzung von kiffis SQLite-library viele dinge vermisste und bugs begegnete, entschloss ich mich meine eigene include zu basteln. ich glaube nicht dass es einen gewichtigen geschwindigkeitsnachteil zwischen library und include gibt, wenn überhaupt einen, deshalb sehe ich keinen sinn in einer library.
meine include unterscheidet sich natürlich zu der lib in der handhabung. eine abfrage der datenbank und auswertung der ergebnisse erfolgt optimal durch folgende schleife:
- SQLiteNextCol() wandert immer von der aktuellen position zur letzten spalte, dann auf 0, also #false und beim nächsten aufruf wieder auf die erste position usw.
falls irgendeine prozedur mit einem ungültigem *Table aufgerufen wird, crasht das programm nicht, da vor ausführung jeder solcher prozedur, die speichergröße von *Table auf SizeOF(TABLEMAP) überprüft wird. dies ist die struktur die zur internen orientierung im *Table dient. jede prozedur die *Table als parameter erwartet, erwartet *Table.TABLEMAP, es reicht aber aus den beginn dieser struktur zu übergeben, weshalb *Table(.l) = SQliteGetTable(...) als handle ausreicht. wer möchte kann auch *Table.TABLEMAP = SQliteGetTable(...) verwenden um dann direkt auf auf die werte dieser struktur zugreifen zu können. hier ist zu erwähnen, dass zum leichteren verständnis die position der ersten spalte in der ersten reihe = row1, col1 ist. intern ist diese position row1, col0, was aber sehr verwirrend ist und ich nach längerem hin und her änderte.
auch hin und her bewegte ich mich bei der frage nach dem auslesen der werte einer tabelle an sich. so weit ich das überblicke, liefert SQLite standardmäßig strings zurück, weshalb dies auch das verhalten von SQLiteValue(*Table) ist. ein SQLiteLongValue(*Table) würde nichts anderes machen als intern den von SQLite zurückgelieferten string in long umzuwandeln, weshalb ich die verwendung von Val(SQLiteValue(*Table)) vorziehe. ich bin mir da aber nicht sicher und lasse mich durch aufklärung (um die ich bitte, falls ich mich irre!) noch einmal von hin nach her bewegen.
die restlichen prozeduren lassen sich anhand des beispieles verstehen. schlägt eine prozedur fehl, so kann man mit SQliteErrorCode(hDB.l) den fehlercode abragen und/oder sein programm mit SQliteErrorMessage(hDB.l) kontrolliert crashen lassen.
falls ausreichendes interesse besteht, wird mich das bewegen die include zu erweitern. ansonsten habe ich alles was ich brauche um mein eigentliches projekt weiter zu machen.
zum ausführen der include muss SQLiteInit("...") auf den pfad zur sqlite3.dll verweisen.
meine include unterscheidet sich natürlich zu der lib in der handhabung. eine abfrage der datenbank und auswertung der ergebnisse erfolgt optimal durch folgende schleife:
Code: Alles auswählen
*Table = SQliteGetTable(hDB, "SELECT ...")
If *Table
While SQLiteNextRow(*Table)
While SQLiteNextCol(*Table)
Value$ = SQLiteValue(*Table)
Wend
Wend
SQliteFreeTable(*Table)
EndIf
falls irgendeine prozedur mit einem ungültigem *Table aufgerufen wird, crasht das programm nicht, da vor ausführung jeder solcher prozedur, die speichergröße von *Table auf SizeOF(TABLEMAP) überprüft wird. dies ist die struktur die zur internen orientierung im *Table dient. jede prozedur die *Table als parameter erwartet, erwartet *Table.TABLEMAP, es reicht aber aus den beginn dieser struktur zu übergeben, weshalb *Table(.l) = SQliteGetTable(...) als handle ausreicht. wer möchte kann auch *Table.TABLEMAP = SQliteGetTable(...) verwenden um dann direkt auf auf die werte dieser struktur zugreifen zu können. hier ist zu erwähnen, dass zum leichteren verständnis die position der ersten spalte in der ersten reihe = row1, col1 ist. intern ist diese position row1, col0, was aber sehr verwirrend ist und ich nach längerem hin und her änderte.
auch hin und her bewegte ich mich bei der frage nach dem auslesen der werte einer tabelle an sich. so weit ich das überblicke, liefert SQLite standardmäßig strings zurück, weshalb dies auch das verhalten von SQLiteValue(*Table) ist. ein SQLiteLongValue(*Table) würde nichts anderes machen als intern den von SQLite zurückgelieferten string in long umzuwandeln, weshalb ich die verwendung von Val(SQLiteValue(*Table)) vorziehe. ich bin mir da aber nicht sicher und lasse mich durch aufklärung (um die ich bitte, falls ich mich irre!) noch einmal von hin nach her bewegen.
die restlichen prozeduren lassen sich anhand des beispieles verstehen. schlägt eine prozedur fehl, so kann man mit SQliteErrorCode(hDB.l) den fehlercode abragen und/oder sein programm mit SQliteErrorMessage(hDB.l) kontrolliert crashen lassen.
falls ausreichendes interesse besteht, wird mich das bewegen die include zu erweitern. ansonsten habe ich alles was ich brauche um mein eigentliches projekt weiter zu machen.
zum ausführen der include muss SQLiteInit("...") auf den pfad zur sqlite3.dll verweisen.
Code: Alles auswählen
#SQLITE3_OK = 0 ; Successful Result
#SQLITE3_ERROR = 1 ; SQL error Or missing database
#SQLITE3_INTERNAL = 2 ; An internal logic error in SQLite
#SQLITE3_PERM = 3 ; Access permission denied
#SQLITE3_ABORT = 4 ; Callback routine requested An abort
#SQLITE3_BUSY = 5 ; The database file is locked
#SQLITE3_LOCKED = 6 ; A table in The database is locked
#SQLITE3_NOMEM = 7 ; A malloc() failed
#SQLITE3_READONLY = 8 ; Attempt To write A readonly database
#SQLITE3_INTERRUPT = 9 ; Operation terminated by SQLite_Interrupt()
#SQLITE3_IOERR = 10 ; Some kind of disk I/O error occurred
#SQLITE3_CORRUPT = 11 ; The database disk image is malformed
#SQLITE3_NOTFOUND = 12 ; (internal Only) table Or record not found
#SQLITE3_FULL = 13 ; Insertion failed because database is full
#SQLITE3_CANTOPEN = 14 ; Unable To open The database file
#SQLITE3_PROTOCOL = 15 ; database lock protocol error
#SQLITE3_EMPTY = 16 ; (internal Only) database table is empty
#SQLITE3_SCHEMA = 17 ; The database schema changed
#SQLITE3_TOOBIG = 18 ; Too much Data For one Row of A table
#SQLITE3_CONSTRAINT = 19 ; abort due To contraint violation
#SQLITE3_MISMATCH = 20 ; Data type mismatch
#SQLITE3_MISUSE = 21 ; Library used incorrectly
#SQLITE3_NOLFS = 22 ; Uses OS features not supported on host
#SQLITE3_AUTH = 23 ; Authorization denied
#SQLITE3_ROW = 100 ; sqlite_step() has another Row ready
#SQLITE3_DONE = 101 ; sqlite_step() has finished executing
Structure SQLite3DLL
Handle.l
aggregate_context.l
aggregate_count.l
bind_blob.l
bind_double.l
bind_int.l
bind_int64.l
bind_null.l
bind_parameter_count.l
bind_parameter_index.l
bind_parameter_name.l
bind_text.l
bind_text16.l
busy_handler.l
busy_timeout.l
changes.l
close.l
collation_needed.l
collation_needed16.l
column_blob.l
column_bytes.l
column_bytes16.l
column_count.l
column_decltype.l
column_decltype16.l
column_double.l
column_int.l
column_int64.l
column_name.l
column_name16.l
column_text.l
column_text16.l
column_type.l
commit_hook.l
complete.l
complete16.l
create_collation.l
create_collation16.l
create_function.l
create_function16.l
data_count.l
db_handle.l
errcode.l
errmsg.l
errmsg16.l
exec.l
expired.l
finalize.l
free.l
free_table.l
get_autocommit.l
get_auxdata.l
get_table.l
global_recover.l
interrupt.l
last_insert_rowid.l
libversion.l
libversion_number.l
mprintf.l
open.l
open16.l
prepare.l
prepare16.l
progress_handler.l
reset.l
result_blob.l
result_double.l
result_error.l
result_error16.l
result_int.l
result_int64.l
result_null.l
result_text.l
result_text16.l
result_text16be.l
result_text16le.l
result_value.l
set_authorizer.l
set_auxdata.l
snprintf.l
Step.l
total_changes.l
trace.l
transfer_bindings.l
user_data.l
value_blob.l
value_bytes.l
value_bytes16.l
value_double.l
value_int.l
value_int64.l
value_text.l
value_text16.l
value_text16be.l
value_text16le.l
value_type.l
vmprintf.l
EndStructure
;
Global SQLite.SQLite3DLL
Structure TABLEMAP
Table.l
Rows.l
Cols.l
RowPos.l
ColPos.l
EndStructure
Procedure MemorySize(*memory)
!If ~ defined _PB_Memory_Heap | defined @f
!extrn _PB_Memory_Heap
!@@:
!End If
;
!If ~ defined _HeapSize@12 | defined @f
!extrn _HeapSize@12
!@@:
!End If
!MOV Eax, dword [Esp]
!OR Eax, Eax
!JZ @f
!PUSH Eax
!PUSH 0
!PUSH dword [_PB_Memory_Heap]
!CALL _HeapSize@12
!CMP Eax, -1
!JE @f
ProcedureReturn
!@@:
EndProcedure
Procedure SQLiteInit(SQLiteDLL$)
SQLite\Handle = OpenLibrary(#PB_Any, SQLiteDLL$)
If ExamineLibraryFunctions(SQLite\Handle)
While NextLibraryFunction()
pos + #SIZEOF_LONG
PokeL(@SQLite+pos, LibraryFunctionAddress())
Wend
If pos = CountLibraryFunctions(SQLite\Handle) * #SIZEOF_LONG
ProcedureReturn SQLite\Handle
EndIf
EndIf
EndProcedure
Procedure SQLiteEnd()
ProcedureReturn CloseLibrary(SQLite\Handle)
EndProcedure
Procedure SQLiteOpen(DB$)
If CallCFunctionFast(SQLite\open, DB$, @hDB.l) = #SQLITE3_OK
ProcedureReturn hDB
EndIf
EndProcedure
Procedure SQLiteClose(hDB.l)
If CallCFunctionFast(SQLite\close, hDB) = #SQLITE3_OK
ProcedureReturn #True
EndIf
EndProcedure
Procedure SQliteErrorCode(hDB.l)
ProcedureReturn CallCFunctionFast(SQLite\errcode, hDB)
EndProcedure
Procedure$ SQliteErrorMessage(hDB.l)
ProcedureReturn PeekS(CallCFunctionFast(SQLite\errmsg, hDB))
EndProcedure
Procedure SQliteExecute(hDB.l, Statement$)
If CallCFunctionFast(SQLite\exec, hDB, Statement$, #Null, #Null, #Null) = #SQLITE3_OK
ProcedureReturn #True
EndIf
EndProcedure
Procedure SQliteGetTable(hDB.l, Statement$)
If hDB And CallCFunctionFast(SQLite\get_table, hDB, Statement$, @*Table.l, @Rows.l, @Cols.l, #Null) = #SQLITE3_OK
*result = AllocateMemory(SizeOf(TABLEMAP))
PokeL(*result, *Table);Table
PokeL(*result+4, Rows);Rows
PokeL(*result+8, Cols);Cols
PokeL(*result+12, 0) ;RowPos
PokeL(*result+16, 0) ;ColPos
ProcedureReturn *result
EndIf
EndProcedure
Procedure SQliteFreeTable(*Table.TABLEMAP)
If MemorySize(*Table) = SizeOf(TABLEMAP)
If CallCFunctionFast(SQLite\free_table, *Table\Table) | FreeMemory(*Table)
ProcedureReturn #True
EndIf
EndIf
EndProcedure
Procedure SQLiteNextRow(*Table.TABLEMAP)
If MemorySize(*Table) = SizeOf(TABLEMAP)
If *Table\RowPos < *Table\Rows
*Table\RowPos + 1
ProcedureReturn #True
EndIf
EndIf
EndProcedure
Procedure SQLiteNextCol(*Table.TABLEMAP)
If MemorySize(*Table) = SizeOf(TABLEMAP)
If *Table\ColPos < *Table\Cols
*Table\ColPos + 1
ProcedureReturn #True
Else
*Table\ColPos = 0
EndIf
EndIf
EndProcedure
Procedure SQLiteSelectRow(*Table.TABLEMAP, Row.l)
If MemorySize(*Table) = SizeOf(TABLEMAP)
If Row > 0 And Row <= *Table\Rows
*Table\RowPos = Row
ProcedureReturn #True
EndIf
EndIf
EndProcedure
Procedure SQLiteSelectCol(*Table.TABLEMAP, Col.l)
If MemorySize(*Table) = SizeOf(TABLEMAP)
If Col > 0 And Col <= *Table\Cols
*Table\ColPos = Col
ProcedureReturn #True
EndIf
EndIf
EndProcedure
Procedure SQLiteSelectPos(*Table.TABLEMAP, Row.l, Col.l)
If MemorySize(*Table) = SizeOf(TABLEMAP)
If Row > 0 And Row <= *Table\Rows And Col > 0 And Col <= *Table\Cols
*Table\RowPos = Row
*Table\ColPos = Col
ProcedureReturn #True
EndIf
EndIf
EndProcedure
Procedure SQLiteResetPos(*Table.TABLEMAP)
If MemorySize(*Table) = SizeOf(TABLEMAP)
*Table\RowPos = 0
*Table\ColPos = 0
EndIf
EndProcedure
Procedure$ SQLiteColName(*Table.TABLEMAP, Col.l)
If MemorySize(*Table) = SizeOf(TABLEMAP)
If Col > 0 And Col <= *Table\Cols
*val.l = PeekL(*Table\Table + ((Col - 1) * #SIZEOF_LONG))
If *val
ProcedureReturn PeekS(*val)
EndIf
EndIf
EndIf
EndProcedure
Procedure$ SQLiteValue(*Table.TABLEMAP)
If MemorySize(*Table) = SizeOf(TABLEMAP)
If *Table\RowPos > 0 And *Table\RowPos <= *Table\Rows And *Table\ColPos > 0 And *Table\ColPos <= *Table\Cols
*val.l = PeekL(*Table\Table + ((*Table\ColPos - 1 + (*Table\RowPos * *Table\Cols)) * #SIZEOF_LONG))
If *val
ProcedureReturn PeekS(*val)
EndIf
EndIf
EndIf
EndProcedure
Procedure$ SQLiteRowValue(*Table.TABLEMAP, Row.l)
If MemorySize(*Table) = SizeOf(TABLEMAP)
If Row > 0 And Row <= *Table\Rows And *Table\Cols > 0 And *Table\Cols <= *Table\Cols
*val.l = PeekL(*Table\Table + ((*Table\Cols - 1 + (Row * *Table\Cols)) * #SIZEOF_LONG))
If *val
ProcedureReturn PeekS(*val)
EndIf
EndIf
EndIf
EndProcedure
Procedure$ SQLiteColValue(*Table.TABLEMAP, Col.l)
If MemorySize(*Table) = SizeOf(TABLEMAP)
If *Table\RowPos > 0 And *Table\RowPos <= *Table\Rows And Col > 0 And Col <= *Table\Cols
*val.l = PeekL(*Table\Table + ((Col - 1 + (*Table\RowPos * *Table\Cols)) * #SIZEOF_LONG))
If *val
ProcedureReturn PeekS(*val)
EndIf
EndIf
EndIf
EndProcedure
Procedure$ SQLiteSelectValue(*Table.TABLEMAP, Row.l, Col.l)
If MemorySize(*Table) = SizeOf(TABLEMAP)
If Row > 0 And Row <= *Table\Rows And Col > 0 And Col <= *Table\Cols
*val.l = PeekL(*Table\Table + ((Col - 1 + (Row * *Table\Cols)) * #SIZEOF_LONG))
If *val
ProcedureReturn PeekS(*val)
EndIf
EndIf
EndIf
EndProcedure
Procedure SQLiteRows(*Table.TABLEMAP)
If MemorySize(*Table) = SizeOf(TABLEMAP)
ProcedureReturn *Table\Rows
EndIf
EndProcedure
Procedure SQLiteCols(*Table.TABLEMAP)
If MemorySize(*Table) = SizeOf(TABLEMAP)
ProcedureReturn *Table\Cols
EndIf
EndProcedure
;=====================
;||| EXAMPLE |||
;;;;;;;;;;;;;;;;;;;;;;
If SQLiteInit("sqlite3.dll") = 0
MessageRequester("Error", "Couldn't init SQLite.dll") : End
EndIf
hDB = SQLiteOpen("test.db")
If hDB = #False
MessageRequester("Error", SQliteErrorMessage(hDB)) : End
EndIf
If SQliteExecute(hDB, "CREATE TABLE testtable (spalte1, spalte2, spalte3)") = #False
Debug SQliteErrorMessage(hDB)
Else
For r = 1 To 5
If SQliteExecute(hDB, "INSERT INTO testtable (spalte1, spalte2, spalte3) VALUES ('r" + Str(r) + "c1', 'r" + Str(r) + "c2', 'r" + Str(r) + "c3')") = #False
Debug SQliteErrorMessage(hDB)
EndIf
Next
EndIf
*Table = SQliteGetTable(hDB, "SELECT * FROM testtable")
If *Table
While SQLiteNextRow(*Table)
i + 1 : Debug "_row " + Str(i) + ":"
While SQLiteNextCol(*Table)
Debug SQLiteValue(*Table)
Wend
Wend
;;;;
;
Debug "_row 1, col 1:"
SQLiteResetPos(*Table)
SQLiteNextRow(*Table)
SQLiteNextCol(*Table)
Debug SQLiteValue(*Table)
;
Debug "_row 2, col 2:"
SQLiteSelectRow(*Table, 2)
SQLiteSelectCol(*Table, 2)
Debug SQLiteValue(*Table)
;
Debug "_row 3, col 3:"
Debug SQLiteSelectValue(*Table, 3, 3)
;
Debug "_row 1, col 3:"
SQLiteSelectPos(*Table, 1, 1)
Debug SQLiteColValue(*Table, SQLiteCols(*Table))
;
Debug "_row 2, col 3:"
Debug SQLiteRowValue(*Table, 2)
;
Debug "_spaltennamen:"
Debug SQLiteColName(*Table, 1)
Debug SQLiteColName(*Table, 2)
Debug SQLiteColName(*Table, 3)
;
;;;;;;;;;;;;;;;;;;;;;;;
SQliteFreeTable(*Table)
EndIf
SQLiteClose(hDB)
SQLiteEnd()