├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md └── Sources_v1.1 ├── BLOB_Sample.ahk ├── BLOB_Sample_Prepared.ahk ├── Class_SQLiteDB.ahk └── SQLiteDB_sample.ahk /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | #packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | App_Data/*.mdf 166 | App_Data/*.ldf 167 | 168 | ############# 169 | ## Windows detritus 170 | ############# 171 | 172 | # Windows image file caches 173 | Thumbs.db 174 | ehthumbs.db 175 | 176 | # Folder config file 177 | Desktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Mac crap 183 | .DS_Store 184 | 185 | 186 | ############# 187 | ## Python 188 | ############# 189 | 190 | *.py[co] 191 | 192 | # Packages 193 | *.egg 194 | *.egg-info 195 | dist/ 196 | build/ 197 | eggs/ 198 | parts/ 199 | var/ 200 | sdist/ 201 | develop-eggs/ 202 | .installed.cfg 203 | 204 | # Installer logs 205 | pip-log.txt 206 | 207 | # Unit test / coverage reports 208 | .coverage 209 | .tox 210 | 211 | #Translations 212 | *.mo 213 | 214 | #Mr Developer 215 | .mr.developer.cfg 216 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Class_SQLiteDB # 2 | 3 | ### AHK SQLite API Wrapper ### 4 | 5 | AHK class providing support to access SQLite databases. 6 | 7 | 8 | ### Additional stuff ### 9 | 10 | - [SQLite documentation](http://www.sqlite.org/docs.html) 11 | - [SQLite download page](http://www.sqlite.org/download.html) 12 | 13 | ### Basic usage ### 14 | 15 | - Create a new instance of the class SQLiteDB calling `MyDB := New SQLiteDB` 16 | - Open your database calling `MyDB.OpenDB(MyDatabaseFilePath)`. If the file doesn't exist, a new database will be created unless you specify "False" as the third parameter. 17 | - MyDB object provides four methods to pass SQL statements to the database: 18 | - `MyDB.Exec(SQL)` 19 | Should be called for all SQL statements which don't return values from the database (e.g. CREATE, INSERT, UPDATE, etc.). 20 | - `MyDB.GetTable(SQL, Table, ...)` 21 | Should be called for SELECT statements whenever you want to get the complete result of the query as a "Table" object for direct access via the row index. All field values will be returned "in their zero-terminated string representation" (and accordingly an empty string for NULL values). 22 | - `MyDB.Query(SQL, RecordSet, ...)` 23 | Should be called for SELECT statements whenever you want to get the result of the query as a "RecordSet" object. You'll have to call the built-in method `RecordSet.Next()` to access the records sequentially. Only `DB-Query()` does handle BLOBs properly. All other field types will be returned as strings (see `DB.GetTable()`). If you don't need the RecordSet anymore, call `RecordSet.Free()` to release the resources. 24 | - `MyDB.StoreBLOB(SQL, BlobArray)` 25 | Should be called whenever BLOBs shall be stored in the database. For each BLOB in the row you have to specify a `?` parameter within the statement. The parameters are numbered automatically from left to right starting with 1. For each parameter you have to pass an object within BlobArray containing the address and the size of the BLOB. 26 | - After all work is done, call `MyDB.CloseDB()` to close the database. For all still existing queries `RecordSet.Free()` will be called internally. 27 | - For further details look at the inline documentation in the class script and the sample scripts, please. 28 | -------------------------------------------------------------------------------- /Sources_v1.1/BLOB_Sample.ahk: -------------------------------------------------------------------------------- 1 | ; ====================================================================================================================== 2 | ; Script Function: Sample script for Class_SQLiteDB.ahk 3 | ; AHK Version: L 1.1.00.00 (U32) 4 | ; Language: English 5 | ; Tested on: Win XPSP3, Win VistaSP2 (32 Bit) 6 | ; Author: just me 7 | ; ====================================================================================================================== 8 | ; AHK Settings 9 | ; ====================================================================================================================== 10 | #NoEnv 11 | ; #Warn 12 | ; #Warn LocalSameAsGlobal, Off 13 | #SingleInstance force 14 | SetWorkingDir, %A_ScriptDir% 15 | SetBatchLines, -1 16 | ; ====================================================================================================================== 17 | ; Includes 18 | #Include Class_SQLiteDB.ahk 19 | ; ====================================================================================================================== 20 | ; Get the Google logo or store a picture named Original.gif in the script's folder and comment this out 21 | FileDelete, Original.gif 22 | URLDownloadToFile, http://www.google.de/intl/de_ALL/images/logos/images_logo_lg.gif, Original.gif 23 | ; ====================================================================================================================== 24 | ; Start 25 | FileDelete, Blob.gif 26 | DBFileName := A_ScriptDir . "\TEST.DB" 27 | If FileExist(DBFileName) { 28 | SB_SetText("Deleting " . DBFileName) 29 | FileDelete, %DBFileName% 30 | } 31 | ; ====================================================================================================================== 32 | ; Use Class SQLiteDB : Create new instance 33 | DB := new SQLiteDB 34 | ; ====================================================================================================================== 35 | ; Use Class SQLiteDB : Open/create database and table, insert a BLOB from a GIF file 36 | If !DB.OpenDB(DBFileName) { 37 | MsgBox, 16, SQLite Error, % "Msg:`t" . DB.ErrorMsg . "`nCode:`t" . DB.ErrorCode 38 | ExitApp 39 | } 40 | HFILE := FileOpen("Original.gif", "r") 41 | Size := HFILE.RawRead(BLOB, HFILE.Length) 42 | HFILE.Close() 43 | If !DB.Exec("CREATE TABLE Test (TextType, BlobType);") 44 | MsgBox, 16, SQLite Error, % "Msg:`t" . DB.ErrorMsg . "`nCode:`t" . DB.ErrorCode 45 | DB.Exec("BEGIN TRANSACTION;") 46 | ; ? stands for an automatically numbered parameter (here: 1) to use in BlobArray 47 | SQL := "INSERT INTO Test VALUES('Text', ?);" 48 | ; Create the BLOB array 49 | BlobArray := [] 50 | BlobArray.Insert({Addr: &BLOB, Size: Size}) ; will be inserted as element 1 51 | If !DB.StoreBLOB(SQL, BlobArray) 52 | MsgBox, 16, SQLite Error, % "Msg:`t" . DB.ErrorMsg . "`nCode:`t" . DB.ErrorCode 53 | DB.Exec("COMMIT TRANSACTION;") 54 | ; ====================================================================================================================== 55 | ; Start of query using Query() : Get the BLOB from table Test 56 | HFILE := FileOpen("Blob.gif", "w") 57 | If !DB.Query("SELECT * FROM Test;", RecordSet) 58 | MsgBox, 16, SQLite Error: Query, % "Msg:`t" . RecordSet.ErrorMsg . "`nCode:`t" . RecordSet.ErrorCode 59 | If (RecordSet.HasRows) { 60 | If (RecordSet.Next(Row) < 1) { 61 | MsgBox, 16, %A_ThisFunc%, % "Msg:`t" . RecordSet.ErrorMsg . "`nCode:`t" . RecordSet.ErrorCode 62 | Return 63 | } 64 | Loop, % RecordSet.ColumnCount { 65 | If IsObject(Row[A_Index]) { 66 | Size := Row[A_Index].Size 67 | Addr := Row[A_Index].GetAddress("Blob") 68 | If !(Addr) || !(Size) { 69 | MsgBox, 0, Error, BlobAddr = %Addr% - BlobSize = %Size% 70 | } Else { 71 | VarSetCapacity(MyBLOBVar, Size) ; added 72 | DllCall("Kernel32.dll\RtlMoveMemory", "Ptr", &MyBLOBVar, "Ptr", Addr, "Ptr", Size) ; added 73 | HFILE.RawWrite(&MyBLOBVar, Size) ; changed 74 | ; HFILE.RawWrite(Addr + 0, Size) ; original 75 | } 76 | } 77 | } 78 | } 79 | RecordSet.Free() 80 | HFILE.Close() 81 | RecordSet.Free() 82 | ; ====================================================================================================================== 83 | ; Show the result 84 | Gui, Margin, 20, 20 85 | Gui, Add, Text, Section, Original from Google: 86 | Gui, Add, Pic, y+5, Original.gif 87 | Gui, Add, Text, ys, Copy out of database: 88 | Gui, Add, Pic, y+5, Blob.gif 89 | Gui, Show, , Pictures 90 | FileDelete, Original.gif 91 | FileDelete, Blob.gif 92 | Return 93 | GuiClose: 94 | GuiEscape: 95 | ExitApp -------------------------------------------------------------------------------- /Sources_v1.1/BLOB_Sample_Prepared.ahk: -------------------------------------------------------------------------------- 1 | ; ====================================================================================================================== 2 | ; Script Function: Sample script for Class_SQLiteDB.ahk 3 | ; AHK Version: L 1.1.00.00 (U32) 4 | ; Language: English 5 | ; Tested on: Win XPSP3, Win VistaSP2 (32 Bit) 6 | ; Author: just me 7 | ; ====================================================================================================================== 8 | ; AHK Settings 9 | ; ====================================================================================================================== 10 | #NoEnv 11 | ; #Warn 12 | ; #Warn LocalSameAsGlobal, Off 13 | #SingleInstance force 14 | SetWorkingDir, %A_ScriptDir% 15 | SetBatchLines, -1 16 | ; ====================================================================================================================== 17 | ; Includes 18 | #Include Class_SQLiteDB.ahk 19 | ; ====================================================================================================================== 20 | ; Get the Google logo or store a picture named Original.gif in the script's folder and comment this out 21 | FileDelete, Original.gif 22 | URLDownloadToFile, http://www.google.de/intl/de_ALL/images/logos/images_logo_lg.gif, Original.gif 23 | ; ====================================================================================================================== 24 | ; Start 25 | FileDelete, Blob.gif 26 | DBFileName := A_ScriptDir . "\TEST.DB" 27 | If FileExist(DBFileName) { 28 | SB_SetText("Deleting " . DBFileName) 29 | FileDelete, %DBFileName% 30 | } 31 | ; ====================================================================================================================== 32 | ; Use Class SQLiteDB : Create new instance 33 | DB := New SQLiteDB 34 | ; ====================================================================================================================== 35 | ; Use Class SQLiteDB : Open/create database and table, insert a BLOB from a GIF file 36 | If !DB.OpenDB(DBFileName) { 37 | MsgBox, 16, SQLite Error, % "Msg:`t" . DB.ErrorMsg . "`nCode:`t" . DB.ErrorCode 38 | ExitApp 39 | } 40 | HFILE := FileOpen("Original.gif", "r") 41 | Size := HFILE.RawRead(BLOB, HFILE.Length) 42 | HFILE.Close() 43 | If !DB.Exec("CREATE TABLE Test (TextType, BlobType);") 44 | MsgBox, 16, SQLite Error, % "Msg:`t" . DB.ErrorMsg . "`nCode:`t" . DB.ErrorCode 45 | DB.Exec("BEGIN TRANSACTION;") 46 | ; ? stands for an automatically numbered SQL parameter (here: 1) 47 | SQL := "INSERT INTO Test VALUES('Text', ?);" 48 | ; Bind BLOB 49 | If !DB.Prepare(SQL, ST) 50 | MsgBox, 16, DB.Prepare, % "Msg:`t" . DB.ErrorMsg . "`nCode:`t" . DB.ErrorCode 51 | If !ST.Bind(1, "Blob", &Blob, Size) 52 | MsgBox, 16, ST.Bind, % "Msg:`t" . DB.ErrorMsg . "`nCode:`t" . DB.ErrorCode 53 | If !ST.Step() 54 | MsgBox, 16, ST.Step, % "Msg:`t" . DB.ErrorMsg . "`nCode:`t" . DB.ErrorCode 55 | If !ST.Reset() 56 | MsgBox, 16, ST.Reset, % "Msg:`t" . DB.ErrorMsg . "`nCode:`t" . DB.ErrorCode 57 | If !ST.Free() 58 | MsgBox, 16, ST.Free, % "Msg:`t" . DB.ErrorMsg . "`nCode:`t" . DB.ErrorCode 59 | DB.Exec("COMMIT TRANSACTION;") 60 | ; ====================================================================================================================== 61 | ; Start of query using Query() : Get the BLOB from table Test 62 | HFILE := FileOpen("Blob.gif", "w") 63 | If !DB.Query("SELECT * FROM Test;", RecordSet) 64 | MsgBox, 16, SQLite Error: Query, % "Msg:`t" . RecordSet.ErrorMsg . "`nCode:`t" . RecordSet.ErrorCode 65 | If (RecordSet.HasRows) { 66 | If (RecordSet.Next(Row) < 1) { 67 | MsgBox, 16, %A_ThisFunc%, % "Msg:`t" . RecordSet.ErrorMsg . "`nCode:`t" . RecordSet.ErrorCode 68 | Return 69 | } 70 | Loop, % RecordSet.ColumnCount { 71 | If IsObject(Row[A_Index]) { 72 | Size := Row[A_Index].Size 73 | Addr := Row[A_Index].GetAddress("Blob") 74 | If !(Addr) || !(Size) { 75 | MsgBox, 0, Error, BlobAddr = %Addr% - BlobSize = %Size% 76 | } Else { 77 | VarSetCapacity(MyBLOBVar, Size) ; added 78 | DllCall("Kernel32.dll\RtlMoveMemory", "Ptr", &MyBLOBVar, "Ptr", Addr, "Ptr", Size) ; added 79 | HFILE.RawWrite(&MyBLOBVar, Size) ; changed 80 | ; HFILE.RawWrite(Addr + 0, Size) ; original 81 | } 82 | } 83 | } 84 | } 85 | RecordSet.Free() 86 | HFILE.Close() 87 | RecordSet.Free() 88 | ; ====================================================================================================================== 89 | ; Show the result 90 | Gui, Margin, 20, 20 91 | Gui, Add, Text, Section, Original from Google: 92 | Gui, Add, Pic, y+5, Original.gif 93 | Gui, Add, Text, ys, Copy out of database: 94 | Gui, Add, Pic, y+5, Blob.gif 95 | Gui, Show, , Pictures 96 | FileDelete, Original.gif 97 | FileDelete, Blob.gif 98 | Return 99 | GuiClose: 100 | GuiEscape: 101 | ExitApp -------------------------------------------------------------------------------- /Sources_v1.1/Class_SQLiteDB.ahk: -------------------------------------------------------------------------------- 1 | ; ====================================================================================================================== 2 | ; Function: Class definitions as wrappers for SQLite3.dll to work with SQLite DBs. 3 | ; AHK version: 1.1.33.09 4 | ; Tested on: Win 10 Pro (x64), SQLite 3.11.1 5 | ; Version: 0.0.01.00/2011-08-10/just me 6 | ; 0.0.02.00/2012-08-10/just me - Added basic BLOB support 7 | ; 0.0.03.00/2012-08-11/just me - Added more advanced BLOB support 8 | ; 0.0.04.00/2013-06-29/just me - Added new methods AttachDB and DetachDB 9 | ; 0.0.05.00/2013-08-03/just me - Changed base class assignment 10 | ; 0.0.06.00/2016-01-28/just me - Fixed version check, revised parameter initialization. 11 | ; 0.0.07.00/2016-03-28/just me - Added support for PRAGMA statements. 12 | ; 0.0.08.00/2019-03-09/just me - Added basic support for application-defined functions 13 | ; 0.0.09.00/2019-07-09/just me - Added basic support for prepared statements, minor bug fixes 14 | ; 0.0.10.00/2019-12-12/just me - Fixed bug in EscapeStr method 15 | ; 0.0.11.00/2021-10-10/just me - Removed statement checks in GetTable, Prepare, and Query 16 | ; 0.0.12.00/2022-09-18/just me - Fixed bug for Bind - type text 17 | ; 0.0.13.00/2022-10-03/just me - Fixed bug in Prepare 18 | ; 0.0.14.00/2022-10-04/just me - Changed DllCall parameter type PtrP to UPtrP 19 | ; Remarks: Names of "private" properties / methods are prefixed with an underscore, 20 | ; they must not be set / called by the script! 21 | ; 22 | ; SQLite3.dll file is assumed to be in the script's folder, otherwise you have to 23 | ; provide an INI-File SQLiteDB.ini in the script's folder containing the path: 24 | ; [Main] 25 | ; DllPath=Path to SQLite3.dll 26 | ; 27 | ; Encoding of SQLite DBs is assumed to be UTF-8 28 | ; Minimum supported SQLite3.dll version is 3.6 29 | ; Download the current version of SQLite3.dll (and also SQlite3.exe) from www.sqlite.org 30 | ; ====================================================================================================================== 31 | ; This software is provided 'as-is', without any express or implied warranty. 32 | ; In no event will the authors be held liable for any damages arising from the 33 | ; use of this software. 34 | ; ====================================================================================================================== 35 | ; CLASS SQliteDB - SQLiteDB main class 36 | ; ====================================================================================================================== 37 | Class SQLiteDB { 38 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 39 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 40 | ; PRIVATE Properties and Methods ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 41 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 42 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 43 | Static Version := "" 44 | Static _SQLiteDLL := A_ScriptDir . "\SQLite3.dll" 45 | Static _RefCount := 0 46 | Static _MinVersion := "3.6" 47 | ; =================================================================================================================== 48 | ; CLASS _Table 49 | ; Object returned from method GetTable() 50 | ; _Table is an independent object and does not need SQLite after creation at all. 51 | ; =================================================================================================================== 52 | Class _Table { 53 | ; ---------------------------------------------------------------------------------------------------------------- 54 | ; CONSTRUCTOR Create instance variables 55 | ; ---------------------------------------------------------------------------------------------------------------- 56 | __New() { 57 | This.ColumnCount := 0 ; Number of columns in the result table (Integer) 58 | This.RowCount := 0 ; Number of rows in the result table (Integer) 59 | This.ColumnNames := [] ; Names of columns in the result table (Array) 60 | This.Rows := [] ; Rows of the result table (Array of Arrays) 61 | This.HasNames := False ; Does var ColumnNames contain names? (Bool) 62 | This.HasRows := False ; Does var Rows contain rows? (Bool) 63 | This._CurrentRow := 0 ; Row index of last returned row (Integer) 64 | } 65 | ; ---------------------------------------------------------------------------------------------------------------- 66 | ; METHOD GetRow Get row for RowIndex 67 | ; Parameters: RowIndex - Index of the row to retrieve, the index of the first row is 1 68 | ; ByRef Row - Variable to pass out the row array 69 | ; Return values: On failure - False 70 | ; On success - True, Row contains a valid array 71 | ; Remarks: _CurrentRow is set to RowIndex, so a subsequent call of NextRow() will return the 72 | ; following row. 73 | ; ---------------------------------------------------------------------------------------------------------------- 74 | GetRow(RowIndex, ByRef Row) { 75 | Row := "" 76 | If (RowIndex < 1 || RowIndex > This.RowCount) 77 | Return False 78 | If !This.Rows.HasKey(RowIndex) 79 | Return False 80 | Row := This.Rows[RowIndex] 81 | This._CurrentRow := RowIndex 82 | Return True 83 | } 84 | ; ---------------------------------------------------------------------------------------------------------------- 85 | ; METHOD Next Get next row depending on _CurrentRow 86 | ; Parameters: ByRef Row - Variable to pass out the row array 87 | ; Return values: On failure - False, -1 for EOR (end of rows) 88 | ; On success - True, Row contains a valid array 89 | ; ---------------------------------------------------------------------------------------------------------------- 90 | Next(ByRef Row) { 91 | Row := "" 92 | If (This._CurrentRow >= This.RowCount) 93 | Return -1 94 | This._CurrentRow += 1 95 | If !This.Rows.HasKey(This._CurrentRow) 96 | Return False 97 | Row := This.Rows[This._CurrentRow] 98 | Return True 99 | } 100 | ; ---------------------------------------------------------------------------------------------------------------- 101 | ; METHOD Reset Reset _CurrentRow to zero 102 | ; Parameters: None 103 | ; Return value: True 104 | ; ---------------------------------------------------------------------------------------------------------------- 105 | Reset() { 106 | This._CurrentRow := 0 107 | Return True 108 | } 109 | } 110 | ; =================================================================================================================== 111 | ; CLASS _RecordSet 112 | ; Object returned from method Query() 113 | ; The records (rows) of a recordset can be accessed sequentially per call of Next() starting with the first record. 114 | ; After a call of Reset() calls of Next() will start with the first record again. 115 | ; When the recordset isn't needed any more, call Free() to free the resources. 116 | ; The lifetime of a recordset depends on the lifetime of the related SQLiteDB object. 117 | ; =================================================================================================================== 118 | Class _RecordSet { 119 | ; ---------------------------------------------------------------------------------------------------------------- 120 | ; CONSTRUCTOR Create instance variables 121 | ; ---------------------------------------------------------------------------------------------------------------- 122 | __New() { 123 | This.ColumnCount := 0 ; Number of columns (Integer) 124 | This.ColumnNames := [] ; Names of columns in the result table (Array) 125 | This.HasNames := False ; Does var ColumnNames contain names? (Bool) 126 | This.HasRows := False ; Does _RecordSet contain rows? (Bool) 127 | This.CurrentRow := 0 ; Index of current row (Integer) 128 | This.ErrorMsg := "" ; Last error message (String) 129 | This.ErrorCode := 0 ; Last SQLite error code / ErrorLevel (Variant) 130 | This._Handle := 0 ; Query handle (Pointer) 131 | This._DB := {} ; SQLiteDB object (Object) 132 | } 133 | ; ---------------------------------------------------------------------------------------------------------------- 134 | ; DESTRUCTOR Clear instance variables 135 | ; ---------------------------------------------------------------------------------------------------------------- 136 | __Delete() { 137 | If (This._Handle) 138 | This.Free() 139 | } 140 | ; ---------------------------------------------------------------------------------------------------------------- 141 | ; METHOD Next Get next row of query result 142 | ; Parameters: ByRef Row - Variable to store the row array 143 | ; Return values: On success - True, Row contains the row array 144 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 145 | ; -1 for EOR (end of records) 146 | ; ---------------------------------------------------------------------------------------------------------------- 147 | Next(ByRef Row) { 148 | Static SQLITE_NULL := 5 149 | Static SQLITE_BLOB := 4 150 | Static EOR := -1 151 | Row := "" 152 | This.ErrorMsg := "" 153 | This.ErrorCode := 0 154 | If !(This._Handle) { 155 | This.ErrorMsg := "Invalid query handle!" 156 | Return False 157 | } 158 | RC := DllCall("SQlite3.dll\sqlite3_step", "Ptr", This._Handle, "Cdecl Int") 159 | If (ErrorLevel) { 160 | This.ErrorMsg := "DllCall sqlite3_step failed!" 161 | This.ErrorCode := ErrorLevel 162 | Return False 163 | } 164 | If (RC <> This._DB._ReturnCode("SQLITE_ROW")) { 165 | If (RC = This._DB._ReturnCode("SQLITE_DONE")) { 166 | This.ErrorMsg := "EOR" 167 | This.ErrorCode := RC 168 | Return EOR 169 | } 170 | This.ErrorMsg := This._DB.ErrMsg() 171 | This.ErrorCode := RC 172 | Return False 173 | } 174 | RC := DllCall("SQlite3.dll\sqlite3_data_count", "Ptr", This._Handle, "Cdecl Int") 175 | If (ErrorLevel) { 176 | This.ErrorMsg := "DllCall sqlite3_data_count failed!" 177 | This.ErrorCode := ErrorLevel 178 | Return False 179 | } 180 | If (RC < 1) { 181 | This.ErrorMsg := "Recordset is empty!" 182 | This.ErrorCode := This._DB._ReturnCode("SQLITE_EMPTY") 183 | Return False 184 | } 185 | Row := [] 186 | Loop, %RC% { 187 | Column := A_Index - 1 188 | ColumnType := DllCall("SQlite3.dll\sqlite3_column_type", "Ptr", This._Handle, "Int", Column, "Cdecl Int") 189 | If (ErrorLevel) { 190 | This.ErrorMsg := "DllCall sqlite3_column_type failed!" 191 | This.ErrorCode := ErrorLevel 192 | Return False 193 | } 194 | If (ColumnType = SQLITE_NULL) { 195 | Row[A_Index] := "" 196 | } Else If (ColumnType = SQLITE_BLOB) { 197 | BlobPtr := DllCall("SQlite3.dll\sqlite3_column_blob", "Ptr", This._Handle, "Int", Column, "Cdecl UPtr") 198 | BlobSize := DllCall("SQlite3.dll\sqlite3_column_bytes", "Ptr", This._Handle, "Int", Column, "Cdecl Int") 199 | If (BlobPtr = 0) || (BlobSize = 0) { 200 | Row[A_Index] := "" 201 | } Else { 202 | Row[A_Index] := {} 203 | Row[A_Index].Size := BlobSize 204 | Row[A_Index].Blob := "" 205 | Row[A_Index].SetCapacity("Blob", BlobSize) 206 | Addr := Row[A_Index].GetAddress("Blob") 207 | DllCall("Kernel32.dll\RtlMoveMemory", "Ptr", Addr, "Ptr", BlobPtr, "Ptr", BlobSize) 208 | } 209 | } Else { 210 | StrPtr := DllCall("SQlite3.dll\sqlite3_column_text", "Ptr", This._Handle, "Int", Column, "Cdecl UPtr") 211 | If (ErrorLevel) { 212 | This.ErrorMsg := "DllCall sqlite3_column_text failed!" 213 | This.ErrorCode := ErrorLevel 214 | Return False 215 | } 216 | Row[A_Index] := StrGet(StrPtr, "UTF-8") 217 | } 218 | } 219 | This.CurrentRow += 1 220 | Return True 221 | } 222 | ; ---------------------------------------------------------------------------------------------------------------- 223 | ; METHOD Reset Reset the result pointer 224 | ; Parameters: None 225 | ; Return values: On success - True 226 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 227 | ; Remarks: After a call of this method you can access the query result via Next() again. 228 | ; ---------------------------------------------------------------------------------------------------------------- 229 | Reset() { 230 | This.ErrorMsg := "" 231 | This.ErrorCode := 0 232 | If !(This._Handle) { 233 | This.ErrorMsg := "Invalid query handle!" 234 | Return False 235 | } 236 | RC := DllCall("SQlite3.dll\sqlite3_reset", "Ptr", This._Handle, "Cdecl Int") 237 | If (ErrorLevel) { 238 | This.ErrorMsg := "DllCall sqlite3_reset failed!" 239 | This.ErrorCode := ErrorLevel 240 | Return False 241 | } 242 | If (RC) { 243 | This.ErrorMsg := This._DB._ErrMsg() 244 | This.ErrorCode := RC 245 | Return False 246 | } 247 | This.CurrentRow := 0 248 | Return True 249 | } 250 | ; ---------------------------------------------------------------------------------------------------------------- 251 | ; METHOD Free Free query result 252 | ; Parameters: None 253 | ; Return values: On success - True 254 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 255 | ; Remarks: After the call of this method further access on the query result is impossible. 256 | ; ---------------------------------------------------------------------------------------------------------------- 257 | Free() { 258 | This.ErrorMsg := "" 259 | This.ErrorCode := 0 260 | If !(This._Handle) 261 | Return True 262 | RC := DllCall("SQlite3.dll\sqlite3_finalize", "Ptr", This._Handle, "Cdecl Int") 263 | If (ErrorLevel) { 264 | This.ErrorMsg := "DllCall sqlite3_finalize failed!" 265 | This.ErrorCode := ErrorLevel 266 | Return False 267 | } 268 | If (RC) { 269 | This.ErrorMsg := This._DB._ErrMsg() 270 | This.ErrorCode := RC 271 | Return False 272 | } 273 | This._DB._Queries.Delete(This._Handle) 274 | This._Handle := 0 275 | This._DB := 0 276 | Return True 277 | } 278 | } 279 | ; =================================================================================================================== 280 | ; CLASS _Statement 281 | ; Object returned from method Prepare() 282 | ; The life-cycle of a prepared statement object usually goes like this: 283 | ; 1. Create the prepared statement object (PST) by calling DB.Prepare(). 284 | ; 2. Bind values to parameters using the PST.Bind_*() methods of the statement object. 285 | ; 3. Run the SQL by calling PST.Step() one or more times. 286 | ; 4. Reset the prepared statement using PTS.Reset() then go back to step 2. Do this zero or more times. 287 | ; 5. Destroy the object using PST.Finalize(). 288 | ; The lifetime of a prepared statement depends on the lifetime of the related SQLiteDB object. 289 | ; =================================================================================================================== 290 | Class _Statement { 291 | ; ---------------------------------------------------------------------------------------------------------------- 292 | ; CONSTRUCTOR Create instance variables 293 | ; ---------------------------------------------------------------------------------------------------------------- 294 | __New() { 295 | This.ErrorMsg := "" ; Last error message (String) 296 | This.ErrorCode := 0 ; Last SQLite error code / ErrorLevel (Variant) 297 | This.ParamCount := 0 ; Number of SQL parameters for this statement (Integer) 298 | This._Handle := 0 ; Query handle (Pointer) 299 | This._DB := {} ; SQLiteDB object (Object) 300 | } 301 | ; ---------------------------------------------------------------------------------------------------------------- 302 | ; DESTRUCTOR Clear instance variables 303 | ; ---------------------------------------------------------------------------------------------------------------- 304 | __Delete() { 305 | If (This._Handle) 306 | This.Free() 307 | } 308 | ; ---------------------------------------------------------------------------------------------------------------- 309 | ; METHOD Bind Bind values to SQL parameters. 310 | ; Parameters: Index - 1-based index of the SQL parameter 311 | ; Type - type of the SQL parameter (currently: Blob/Double/Int/Text) 312 | ; Param3 - type dependent value 313 | ; Param4 - type dependent value 314 | ; Param5 - not used 315 | ; Return values: On success - True 316 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 317 | ; ---------------------------------------------------------------------------------------------------------------- 318 | Bind(Index, Type, Param3 := "", Param4 := 0, Param5 := 0) { 319 | Static SQLITE_STATIC := 0 320 | Static SQLITE_TRANSIENT := -1 321 | Static Types := {Blob: 1, Double: 1, Int: 1, Text: 1} 322 | This.ErrorMsg := "" 323 | This.ErrorCode := 0 324 | If !(This._Handle) { 325 | This.ErrorMsg := "Invalid statement handle!" 326 | Return False 327 | } 328 | If (Index < 1) || (Index > This.ParamCount) { 329 | This.ErrorMsg := "Invalid parameter index!" 330 | Return False 331 | } 332 | If (Types[Type] = "") { 333 | This.ErrorMsg := "Invalid parameter type!" 334 | Return False 335 | } 336 | If (Type = "Blob") { ; ---------------------------------------------------------------------------------------- 337 | ; Param3 = BLOB pointer, Param4 = BLOB size in bytes 338 | If Param3 Is Not Integer 339 | { 340 | This.ErrorMsg := "Invalid blob pointer!" 341 | Return False 342 | } 343 | If Param4 Is Not Integer 344 | { 345 | This.ErrorMsg := "Invalid blob size!" 346 | Return False 347 | } 348 | ; Let SQLite always create a copy of the BLOB 349 | RC := DllCall("SQlite3.dll\sqlite3_bind_blob", "Ptr", This._Handle, "Int", Index, "Ptr", Param3 350 | , "Int", Param4, "Ptr", -1, "Cdecl Int") 351 | If (ErrorLeveL) { 352 | This.ErrorMsg := "DllCall sqlite3_bind_blob failed!" 353 | This.ErrorCode := ErrorLevel 354 | Return False 355 | } 356 | If (RC) { 357 | This.ErrorMsg := This._ErrMsg() 358 | This.ErrorCode := RC 359 | Return False 360 | } 361 | } 362 | Else If (Type = "Double") { ; --------------------------------------------------------------------------------- 363 | ; Param3 = double value 364 | If Param3 Is Not Float 365 | { 366 | This.ErrorMsg := "Invalid value for double!" 367 | Return False 368 | } 369 | RC := DllCall("SQlite3.dll\sqlite3_bind_double", "Ptr", This._Handle, "Int", Index, "Double", Param3 370 | , "Cdecl Int") 371 | If (ErrorLeveL) { 372 | This.ErrorMsg := "DllCall sqlite3_bind_double failed!" 373 | This.ErrorCode := ErrorLevel 374 | Return False 375 | } 376 | If (RC) { 377 | This.ErrorMsg := This._ErrMsg() 378 | This.ErrorCode := RC 379 | Return False 380 | } 381 | } 382 | Else If (Type = "Int") { ; ------------------------------------------------------------------------------------ 383 | ; Param3 = integer value 384 | If Param3 Is Not Integer 385 | { 386 | This.ErrorMsg := "Invalid value for int!" 387 | Return False 388 | } 389 | RC := DllCall("SQlite3.dll\sqlite3_bind_int", "Ptr", This._Handle, "Int", Index, "Int", Param3 390 | , "Cdecl Int") 391 | If (ErrorLeveL) { 392 | This.ErrorMsg := "DllCall sqlite3_bind_int failed!" 393 | This.ErrorCode := ErrorLevel 394 | Return False 395 | } 396 | If (RC) { 397 | This.ErrorMsg := This._ErrMsg() 398 | This.ErrorCode := RC 399 | Return False 400 | } 401 | } 402 | Else If (Type = "Text") { ; ----------------------------------------------------------------------------------- 403 | ; Param3 = zero-terminated string 404 | This._DB._StrToUTF8(Param3, UTF8) 405 | ; Let SQLite always create a copy of the text 406 | RC := DllCall("SQlite3.dll\sqlite3_bind_text", "Ptr", This._Handle, "Int", Index, "Ptr", &UTF8 407 | , "Int", -1, "Ptr", -1, "Cdecl Int") 408 | If (ErrorLeveL) { 409 | This.ErrorMsg := "DllCall sqlite3_bind_text failed!" 410 | This.ErrorCode := ErrorLevel 411 | Return False 412 | } 413 | If (RC) { 414 | This.ErrorMsg := This._ErrMsg() 415 | This.ErrorCode := RC 416 | Return False 417 | } 418 | } 419 | Return True 420 | } 421 | 422 | ; ---------------------------------------------------------------------------------------------------------------- 423 | ; METHOD Step Evaluate the prepared statement. 424 | ; Parameters: None 425 | ; Return values: On success - True 426 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 427 | ; Remarks: You must call ST.Reset() before you can call ST.Step() again. 428 | ; ---------------------------------------------------------------------------------------------------------------- 429 | Step() { 430 | This.ErrorMsg := "" 431 | This.ErrorCode := 0 432 | If !(This._Handle) { 433 | This.ErrorMsg := "Invalid statement handle!" 434 | Return False 435 | } 436 | RC := DllCall("SQlite3.dll\sqlite3_step", "Ptr", This._Handle, "Cdecl Int") 437 | If (ErrorLevel) { 438 | This.ErrorMsg := "DllCall sqlite3_step failed!" 439 | This.ErrorCode := ErrorLevel 440 | Return False 441 | } 442 | If (RC <> This._DB._ReturnCode("SQLITE_DONE")) 443 | && (RC <> This._DB._ReturnCode("SQLITE_ROW")) { 444 | This.ErrorMsg := This._DB.ErrMsg() 445 | This.ErrorCode := RC 446 | Return False 447 | } 448 | Return True 449 | } 450 | ; ---------------------------------------------------------------------------------------------------------------- 451 | ; METHOD Reset Reset the prepared statement. 452 | ; Parameters: ClearBindings - Clear bound SQL parameter values (True/False) 453 | ; Return values: On success - True 454 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 455 | ; Remarks: After a call of this method you can access the query result via Next() again. 456 | ; ---------------------------------------------------------------------------------------------------------------- 457 | Reset(ClearBindings := True) { 458 | This.ErrorMsg := "" 459 | This.ErrorCode := 0 460 | If !(This._Handle) { 461 | This.ErrorMsg := "Invalid statement handle!" 462 | Return False 463 | } 464 | RC := DllCall("SQlite3.dll\sqlite3_reset", "Ptr", This._Handle, "Cdecl Int") 465 | If (ErrorLevel) { 466 | This.ErrorMsg := "DllCall sqlite3_reset failed!" 467 | This.ErrorCode := ErrorLevel 468 | Return False 469 | } 470 | If (RC) { 471 | This.ErrorMsg := This._DB._ErrMsg() 472 | This.ErrorCode := RC 473 | Return False 474 | } 475 | If (ClearBindings) { 476 | RC := DllCall("SQlite3.dll\sqlite3_clear_bindings", "Ptr", This._Handle, "Cdecl Int") 477 | If (ErrorLevel) { 478 | This.ErrorMsg := "DllCall sqlite3_clear_bindings failed!" 479 | This.ErrorCode := ErrorLevel 480 | Return False 481 | } 482 | If (RC) { 483 | This.ErrorMsg := This._DB._ErrMsg() 484 | This.ErrorCode := RC 485 | Return False 486 | } 487 | } 488 | Return True 489 | } 490 | ; ---------------------------------------------------------------------------------------------------------------- 491 | ; METHOD Free Free the prepared statement object. 492 | ; Parameters: None 493 | ; Return values: On success - True 494 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 495 | ; Remarks: After the call of this method further access on the statement object is impossible. 496 | ; ---------------------------------------------------------------------------------------------------------------- 497 | Free() { 498 | This.ErrorMsg := "" 499 | This.ErrorCode := 0 500 | If !(This._Handle) 501 | Return True 502 | RC := DllCall("SQlite3.dll\sqlite3_finalize", "Ptr", This._Handle, "Cdecl Int") 503 | If (ErrorLevel) { 504 | This.ErrorMsg := "DllCall sqlite3_finalize failed!" 505 | This.ErrorCode := ErrorLevel 506 | Return False 507 | } 508 | If (RC) { 509 | This.ErrorMsg := This._DB._ErrMsg() 510 | This.ErrorCode := RC 511 | Return False 512 | } 513 | This._DB._Stmts.Delete(This._Handle) 514 | This._Handle := 0 515 | This._DB := 0 516 | Return True 517 | } 518 | } 519 | ; =================================================================================================================== 520 | ; CONSTRUCTOR __New 521 | ; =================================================================================================================== 522 | __New() { 523 | This._Path := "" ; Database path (String) 524 | This._Handle := 0 ; Database handle (Pointer) 525 | This._Queries := {} ; Valid queries (Object) 526 | This._Stmts := {} ; Valid prepared statements (Object) 527 | If (This.Base._RefCount = 0) { 528 | SQLiteDLL := This.Base._SQLiteDLL 529 | If !FileExist(SQLiteDLL) 530 | If FileExist(A_ScriptDir . "\SQLiteDB.ini") { 531 | IniRead, SQLiteDLL, %A_ScriptDir%\SQLiteDB.ini, Main, DllPath, %SQLiteDLL% 532 | This.Base._SQLiteDLL := SQLiteDLL 533 | } 534 | If !(DLL := DllCall("LoadLibrary", "Str", This.Base._SQLiteDLL, "UPtr")) { 535 | MsgBox, 16, SQLiteDB Error, % "DLL " . SQLiteDLL . " does not exist!" 536 | ExitApp 537 | } 538 | This.Base.Version := StrGet(DllCall("SQlite3.dll\sqlite3_libversion", "Cdecl UPtr"), "UTF-8") 539 | SQLVersion := StrSplit(This.Base.Version, ".") 540 | MinVersion := StrSplit(This.Base._MinVersion, ".") 541 | If (SQLVersion[1] < MinVersion[1]) || ((SQLVersion[1] = MinVersion[1]) && (SQLVersion[2] < MinVersion[2])){ 542 | DllCall("FreeLibrary", "Ptr", DLL) 543 | MsgBox, 16, SQLite ERROR, % "Version " . This.Base.Version . " of SQLite3.dll is not supported!`n`n" 544 | . "You can download the current version from www.sqlite.org!" 545 | ExitApp 546 | } 547 | } 548 | This.Base._RefCount += 1 549 | } 550 | ; =================================================================================================================== 551 | ; DESTRUCTOR __Delete 552 | ; =================================================================================================================== 553 | __Delete() { 554 | If (This._Handle) 555 | This.CloseDB() 556 | This.Base._RefCount -= 1 557 | If (This.Base._RefCount = 0) { 558 | If (DLL := DllCall("GetModuleHandle", "Str", This.Base._SQLiteDLL, "UPtr")) 559 | DllCall("FreeLibrary", "Ptr", DLL) 560 | } 561 | } 562 | ; =================================================================================================================== 563 | ; PRIVATE _StrToUTF8 564 | ; =================================================================================================================== 565 | _StrToUTF8(Str, ByRef UTF8) { 566 | VarSetCapacity(UTF8, StrPut(Str, "UTF-8"), 0) 567 | StrPut(Str, &UTF8, "UTF-8") 568 | Return &UTF8 569 | } 570 | ; =================================================================================================================== 571 | ; PRIVATE _UTF8ToStr 572 | ; =================================================================================================================== 573 | _UTF8ToStr(UTF8) { 574 | Return StrGet(UTF8, "UTF-8") 575 | } 576 | ; =================================================================================================================== 577 | ; PRIVATE _ErrMsg 578 | ; =================================================================================================================== 579 | _ErrMsg() { 580 | If (RC := DllCall("SQLite3.dll\sqlite3_errmsg", "Ptr", This._Handle, "Cdecl UPtr")) 581 | Return StrGet(&RC, "UTF-8") 582 | Return "" 583 | } 584 | ; =================================================================================================================== 585 | ; PRIVATE _ErrCode 586 | ; =================================================================================================================== 587 | _ErrCode() { 588 | Return DllCall("SQLite3.dll\sqlite3_errcode", "Ptr", This._Handle, "Cdecl Int") 589 | } 590 | ; =================================================================================================================== 591 | ; PRIVATE _Changes 592 | ; =================================================================================================================== 593 | _Changes() { 594 | Return DllCall("SQLite3.dll\sqlite3_changes", "Ptr", This._Handle, "Cdecl Int") 595 | } 596 | ; =================================================================================================================== 597 | ; PRIVATE _Returncode 598 | ; =================================================================================================================== 599 | _ReturnCode(RC) { 600 | Static RCODE := {SQLITE_OK: 0 ; Successful result 601 | , SQLITE_ERROR: 1 ; SQL error or missing database 602 | , SQLITE_INTERNAL: 2 ; NOT USED. Internal logic error in SQLite 603 | , SQLITE_PERM: 3 ; Access permission denied 604 | , SQLITE_ABORT: 4 ; Callback routine requested an abort 605 | , SQLITE_BUSY: 5 ; The database file is locked 606 | , SQLITE_LOCKED: 6 ; A table in the database is locked 607 | , SQLITE_NOMEM: 7 ; A malloc() failed 608 | , SQLITE_READONLY: 8 ; Attempt to write a readonly database 609 | , SQLITE_INTERRUPT: 9 ; Operation terminated by sqlite3_interrupt() 610 | , SQLITE_IOERR: 10 ; Some kind of disk I/O error occurred 611 | , SQLITE_CORRUPT: 11 ; The database disk image is malformed 612 | , SQLITE_NOTFOUND: 12 ; NOT USED. Table or record not found 613 | , SQLITE_FULL: 13 ; Insertion failed because database is full 614 | , SQLITE_CANTOPEN: 14 ; Unable to open the database file 615 | , SQLITE_PROTOCOL: 15 ; NOT USED. Database lock protocol error 616 | , SQLITE_EMPTY: 16 ; Database is empty 617 | , SQLITE_SCHEMA: 17 ; The database schema changed 618 | , SQLITE_TOOBIG: 18 ; String or BLOB exceeds size limit 619 | , SQLITE_CONSTRAINT: 19 ; Abort due to constraint violation 620 | , SQLITE_MISMATCH: 20 ; Data type mismatch 621 | , SQLITE_MISUSE: 21 ; Library used incorrectly 622 | , SQLITE_NOLFS: 22 ; Uses OS features not supported on host 623 | , SQLITE_AUTH: 23 ; Authorization denied 624 | , SQLITE_FORMAT: 24 ; Auxiliary database format error 625 | , SQLITE_RANGE: 25 ; 2nd parameter to sqlite3_bind out of range 626 | , SQLITE_NOTADB: 26 ; File opened that is not a database file 627 | , SQLITE_ROW: 100 ; sqlite3_step() has another row ready 628 | , SQLITE_DONE: 101} ; sqlite3_step() has finished executing 629 | Return RCODE.HasKey(RC) ? RCODE[RC] : "" 630 | } 631 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 632 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 633 | ; PUBLIC Interface ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 634 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 635 | ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 636 | ; =================================================================================================================== 637 | ; Properties 638 | ; =================================================================================================================== 639 | ErrorMsg := "" ; Error message (String) 640 | ErrorCode := 0 ; SQLite error code / ErrorLevel (Variant) 641 | Changes := 0 ; Changes made by last call of Exec() (Integer) 642 | SQL := "" ; Last executed SQL statement (String) 643 | ; =================================================================================================================== 644 | ; METHOD OpenDB Open a database 645 | ; Parameters: DBPath - Path of the database file 646 | ; Access - Wanted access: "R"ead / "W"rite 647 | ; Create - Create new database in write mode, if it doesn't exist 648 | ; Return values: On success - True 649 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 650 | ; Remarks: If DBPath is empty in write mode, a database called ":memory:" is created in memory 651 | ; and deletet on call of CloseDB. 652 | ; =================================================================================================================== 653 | OpenDB(DBPath, Access := "W", Create := True) { 654 | Static SQLITE_OPEN_READONLY := 0x01 ; Database opened as read-only 655 | Static SQLITE_OPEN_READWRITE := 0x02 ; Database opened as read-write 656 | Static SQLITE_OPEN_CREATE := 0x04 ; Database will be created if not exists 657 | Static MEMDB := ":memory:" 658 | This.ErrorMsg := "" 659 | This.ErrorCode := 0 660 | HDB := 0 661 | If (DBPath = "") 662 | DBPath := MEMDB 663 | If (DBPath = This._Path) && (This._Handle) 664 | Return True 665 | If (This._Handle) { 666 | This.ErrorMsg := "You must first close DB " . This._Path . "!" 667 | Return False 668 | } 669 | Flags := 0 670 | Access := SubStr(Access, 1, 1) 671 | If (Access <> "W") && (Access <> "R") 672 | Access := "R" 673 | Flags := SQLITE_OPEN_READONLY 674 | If (Access = "W") { 675 | Flags := SQLITE_OPEN_READWRITE 676 | If (Create) 677 | Flags |= SQLITE_OPEN_CREATE 678 | } 679 | This._Path := DBPath 680 | This._StrToUTF8(DBPath, UTF8) 681 | RC := DllCall("SQlite3.dll\sqlite3_open_v2", "Ptr", &UTF8, "UPtrP", HDB, "Int", Flags, "Ptr", 0, "Cdecl Int") 682 | If (ErrorLevel) { 683 | This._Path := "" 684 | This.ErrorMsg := "DLLCall sqlite3_open_v2 failed!" 685 | This.ErrorCode := ErrorLevel 686 | Return False 687 | } 688 | If (RC) { 689 | This._Path := "" 690 | This.ErrorMsg := This._ErrMsg() 691 | This.ErrorCode := RC 692 | Return False 693 | } 694 | This._Handle := HDB 695 | Return True 696 | } 697 | ; =================================================================================================================== 698 | ; METHOD CloseDB Close database 699 | ; Parameters: None 700 | ; Return values: On success - True 701 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 702 | ; =================================================================================================================== 703 | CloseDB() { 704 | This.ErrorMsg := "" 705 | This.ErrorCode := 0 706 | This.SQL := "" 707 | If !(This._Handle) 708 | Return True 709 | For Each, Query in This._Queries 710 | DllCall("SQlite3.dll\sqlite3_finalize", "Ptr", Query, "Cdecl Int") 711 | RC := DllCall("SQlite3.dll\sqlite3_close", "Ptr", This._Handle, "Cdecl Int") 712 | If (ErrorLevel) { 713 | This.ErrorMsg := "DLLCall sqlite3_close failed!" 714 | This.ErrorCode := ErrorLevel 715 | Return False 716 | } 717 | If (RC) { 718 | This.ErrorMsg := This._ErrMsg() 719 | This.ErrorCode := RC 720 | Return False 721 | } 722 | This._Path := "" 723 | This._Handle := "" 724 | This._Queries := [] 725 | Return True 726 | } 727 | ; =================================================================================================================== 728 | ; METHOD AttachDB Add another database file to the current database connection 729 | ; http://www.sqlite.org/lang_attach.html 730 | ; Parameters: DBPath - Path of the database file 731 | ; DBAlias - Database alias name used internally by SQLite 732 | ; Return values: On success - True 733 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 734 | ; =================================================================================================================== 735 | AttachDB(DBPath, DBAlias) { 736 | Return This.Exec("ATTACH DATABASE '" . DBPath . "' As " . DBAlias . ";") 737 | } 738 | ; =================================================================================================================== 739 | ; METHOD DetachDB Detaches an additional database connection previously attached using AttachDB() 740 | ; http://www.sqlite.org/lang_detach.html 741 | ; Parameters: DBAlias - Database alias name used with AttachDB() 742 | ; Return values: On success - True 743 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 744 | ; =================================================================================================================== 745 | DetachDB(DBAlias) { 746 | Return This.Exec("DETACH DATABASE " . DBAlias . ";") 747 | } 748 | ; =================================================================================================================== 749 | ; METHOD Exec Execute SQL statement 750 | ; Parameters: SQL - Valid SQL statement 751 | ; Callback - Name of a callback function to invoke for each result row coming out 752 | ; of the evaluated SQL statements. 753 | ; The function must accept 4 parameters: 754 | ; 1: SQLiteDB object 755 | ; 2: Number of columns 756 | ; 3: Pointer to an array of pointers to columns text 757 | ; 4: Pointer to an array of pointers to column names 758 | ; The address of the current SQL string is passed in A_EventInfo. 759 | ; If the callback function returns non-zero, DB.Exec() returns SQLITE_ABORT 760 | ; without invoking the callback again and without running any subsequent 761 | ; SQL statements. 762 | ; Return values: On success - True, the number of changed rows is given in property Changes 763 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 764 | ; =================================================================================================================== 765 | Exec(SQL, Callback := "") { 766 | This.ErrorMsg := "" 767 | This.ErrorCode := 0 768 | This.SQL := SQL 769 | If !(This._Handle) { 770 | This.ErrorMsg := "Invalid database handle!" 771 | Return False 772 | } 773 | CBPtr := 0 774 | Err := 0 775 | If (FO := Func(Callback)) && (FO.MinParams = 4) 776 | CBPtr := RegisterCallback(Callback, "F C", 4, &SQL) 777 | This._StrToUTF8(SQL, UTF8) 778 | RC := DllCall("SQlite3.dll\sqlite3_exec", "Ptr", This._Handle, "Ptr", &UTF8, "Int", CBPtr, "Ptr", Object(This) 779 | , "UPtrP", Err, "Cdecl Int") 780 | CallError := ErrorLevel 781 | If (CBPtr) 782 | DllCall("Kernel32.dll\GlobalFree", "Ptr", CBPtr) 783 | If (CallError) { 784 | This.ErrorMsg := "DLLCall sqlite3_exec failed!" 785 | This.ErrorCode := CallError 786 | Return False 787 | } 788 | If (RC) { 789 | This.ErrorMsg := StrGet(Err, "UTF-8") 790 | This.ErrorCode := RC 791 | DllCall("SQLite3.dll\sqlite3_free", "Ptr", Err, "Cdecl") 792 | Return False 793 | } 794 | This.Changes := This._Changes() 795 | Return True 796 | } 797 | ; =================================================================================================================== 798 | ; METHOD GetTable Get complete result for SELECT query 799 | ; Parameters: SQL - SQL SELECT statement 800 | ; ByRef TB - Variable to store the result object (TB _Table) 801 | ; MaxResult - Number of rows to return: 802 | ; 0 Complete result (default) 803 | ; -1 Return only RowCount and ColumnCount 804 | ; -2 Return counters and array ColumnNames 805 | ; n Return counters and ColumnNames and first n rows 806 | ; Return values: On success - True, TB contains the result object 807 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 808 | ; =================================================================================================================== 809 | GetTable(SQL, ByRef TB, MaxResult := 0) { 810 | TB := "" 811 | This.ErrorMsg := "" 812 | This.ErrorCode := 0 813 | This.SQL := SQL 814 | If !(This._Handle) { 815 | This.ErrorMsg := "Invalid database handle!" 816 | Return False 817 | } 818 | Names := "" 819 | Err := 0, RC := 0, GetRows := 0 820 | I := 0, Rows := Cols := 0 821 | Table := 0 822 | If MaxResult Is Not Integer 823 | MaxResult := 0 824 | If (MaxResult < -2) 825 | MaxResult := 0 826 | This._StrToUTF8(SQL, UTF8) 827 | RC := DllCall("SQlite3.dll\sqlite3_get_table", "Ptr", This._Handle, "Ptr", &UTF8, "UPtrP", Table 828 | , "IntP", Rows, "IntP", Cols, "UPtrP", Err, "Cdecl Int") 829 | If (ErrorLevel) { 830 | This.ErrorMsg := "DLLCall sqlite3_get_table failed!" 831 | This.ErrorCode := ErrorLevel 832 | Return False 833 | } 834 | If (RC) { 835 | This.ErrorMsg := StrGet(Err, "UTF-8") 836 | This.ErrorCode := RC 837 | DllCall("SQLite3.dll\sqlite3_free", "Ptr", Err, "Cdecl") 838 | Return False 839 | } 840 | TB := new This._Table 841 | TB.ColumnCount := Cols 842 | TB.RowCount := Rows 843 | If (MaxResult = -1) { 844 | DllCall("SQLite3.dll\sqlite3_free_table", "Ptr", Table, "Cdecl") 845 | If (ErrorLevel) { 846 | This.ErrorMsg := "DLLCall sqlite3_free_table failed!" 847 | This.ErrorCode := ErrorLevel 848 | Return False 849 | } 850 | Return True 851 | } 852 | If (MaxResult = -2) 853 | GetRows := 0 854 | Else If (MaxResult > 0) && (MaxResult <= Rows) 855 | GetRows := MaxResult 856 | Else 857 | GetRows := Rows 858 | Offset := 0 859 | Names := Array() 860 | Loop, %Cols% { 861 | Names[A_Index] := StrGet(NumGet(Table+0, Offset, "UPtr"), "UTF-8") 862 | Offset += A_PtrSize 863 | } 864 | TB.ColumnNames := Names 865 | TB.HasNames := True 866 | Loop, %GetRows% { 867 | I := A_Index 868 | TB.Rows[I] := [] 869 | Loop, %Cols% { 870 | TB.Rows[I][A_Index] := StrGet(NumGet(Table+0, Offset, "UPtr"), "UTF-8") 871 | Offset += A_PtrSize 872 | } 873 | } 874 | If (GetRows) 875 | TB.HasRows := True 876 | DllCall("SQLite3.dll\sqlite3_free_table", "Ptr", Table, "Cdecl") 877 | If (ErrorLevel) { 878 | TB := "" 879 | This.ErrorMsg := "DLLCall sqlite3_free_table failed!" 880 | This.ErrorCode := ErrorLevel 881 | Return False 882 | } 883 | Return True 884 | } 885 | ; =================================================================================================================== 886 | ; Prepared statement 10:54 2019.07.05. by Dixtroy 887 | ; DB := new SQLiteDB 888 | ; DB.OpenDB(DBFileName) 889 | ; DB.Prepare 1 or more, just once 890 | ; DB.Step 1 or more on prepared one, repeatable 891 | ; DB.Finalize at the end 892 | ; =================================================================================================================== 893 | ; =================================================================================================================== 894 | ; METHOD Prepare Prepare database table for further actions. 895 | ; Parameters: SQL - SQL statement to be compiled 896 | ; ByRef ST - Variable to store the statement object (Class _Statement) 897 | ; Return values: On success - True, ST contains the statement object 898 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 899 | ; Remarks: You have to pass one ? for each column you want to assign a value later. 900 | ; =================================================================================================================== 901 | Prepare(SQL, ByRef ST) { 902 | This.ErrorMsg := "" 903 | This.ErrorCode := 0 904 | This.SQL := SQL 905 | If !(This._Handle) { 906 | This.ErrorMsg := "Invalid database handle!" 907 | Return False 908 | } 909 | Stmt := 0 910 | This._StrToUTF8(SQL, UTF8) 911 | RC := DllCall("SQlite3.dll\sqlite3_prepare_v2", "Ptr", This._Handle, "Ptr", &UTF8, "Int", -1 912 | , "UPtrP", Stmt, "Ptr", 0, "Cdecl Int") 913 | If (ErrorLeveL) { 914 | This.ErrorMsg := A_ThisFunc . ": DllCall sqlite3_prepare_v2 failed!" 915 | This.ErrorCode := ErrorLevel 916 | Return False 917 | } 918 | If (RC) { 919 | This.ErrorMsg := A_ThisFunc . ": " . This._ErrMsg() 920 | This.ErrorCode := RC 921 | Return False 922 | } 923 | ST := New This._Statement 924 | ST.ParamCount := DllCall("SQlite3.dll\sqlite3_bind_parameter_count", "Ptr", Stmt, "Cdecl Int") 925 | ST._Handle := Stmt 926 | ST._DB := This 927 | This._Stmts[Stmt] := Stmt 928 | Return True 929 | } 930 | ; =================================================================================================================== 931 | ; METHOD Query Get "recordset" object for prepared SELECT query 932 | ; Parameters: SQL - SQL SELECT statement 933 | ; ByRef RS - Variable to store the result object (Class _RecordSet) 934 | ; Return values: On success - True, RS contains the result object 935 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 936 | ; =================================================================================================================== 937 | Query(SQL, ByRef RS) { 938 | RS := "" 939 | This.ErrorMsg := "" 940 | This.ErrorCode := 0 941 | This.SQL := SQL 942 | ColumnCount := 0 943 | HasRows := False 944 | If !(This._Handle) { 945 | This.ErrorMsg := "Invalid dadabase handle!" 946 | Return False 947 | } 948 | Query := 0 949 | This._StrToUTF8(SQL, UTF8) 950 | RC := DllCall("SQlite3.dll\sqlite3_prepare_v2", "Ptr", This._Handle, "Ptr", &UTF8, "Int", -1 951 | , "UPtrP", Query, "Ptr", 0, "Cdecl Int") 952 | If (ErrorLeveL) { 953 | This.ErrorMsg := "DLLCall sqlite3_prepare_v2 failed!" 954 | This.ErrorCode := ErrorLevel 955 | Return False 956 | } 957 | If (RC) { 958 | This.ErrorMsg := This._ErrMsg() 959 | This.ErrorCode := RC 960 | Return False 961 | } 962 | RC := DllCall("SQlite3.dll\sqlite3_column_count", "Ptr", Query, "Cdecl Int") 963 | If (ErrorLevel) { 964 | This.ErrorMsg := "DLLCall sqlite3_column_count failed!" 965 | This.ErrorCode := ErrorLevel 966 | Return False 967 | } 968 | If (RC < 1) { 969 | This.ErrorMsg := "Query result is empty!" 970 | This.ErrorCode := This._ReturnCode("SQLITE_EMPTY") 971 | Return False 972 | } 973 | ColumnCount := RC 974 | Names := [] 975 | Loop, %RC% { 976 | StrPtr := DllCall("SQlite3.dll\sqlite3_column_name", "Ptr", Query, "Int", A_Index - 1, "Cdecl UPtr") 977 | If (ErrorLevel) { 978 | This.ErrorMsg := "DLLCall sqlite3_column_name failed!" 979 | This.ErrorCode := ErrorLevel 980 | Return False 981 | } 982 | Names[A_Index] := StrGet(StrPtr, "UTF-8") 983 | } 984 | RC := DllCall("SQlite3.dll\sqlite3_step", "Ptr", Query, "Cdecl Int") 985 | If (ErrorLevel) { 986 | This.ErrorMsg := "DLLCall sqlite3_step failed!" 987 | This.ErrorCode := ErrorLevel 988 | Return False 989 | } 990 | If (RC = This._ReturnCode("SQLITE_ROW")) 991 | HasRows := True 992 | RC := DllCall("SQlite3.dll\sqlite3_reset", "Ptr", Query, "Cdecl Int") 993 | If (ErrorLevel) { 994 | This.ErrorMsg := "DLLCall sqlite3_reset failed!" 995 | This.ErrorCode := ErrorLevel 996 | Return False 997 | } 998 | RS := new This._RecordSet 999 | RS.ColumnCount := ColumnCount 1000 | RS.ColumnNames := Names 1001 | RS.HasNames := True 1002 | RS.HasRows := HasRows 1003 | RS._Handle := Query 1004 | RS._DB := This 1005 | This._Queries[Query] := Query 1006 | Return True 1007 | } 1008 | ; =================================================================================================================== 1009 | ; METHOD CreateScalarFunc Create a scalar application defined function 1010 | ; Parameters: Name - the name of the function 1011 | ; Args - the number of arguments that the SQL function takes 1012 | ; Func - a pointer to AHK functions that implement the SQL function 1013 | ; Enc - specifies what text encoding this SQL function prefers for its parameters 1014 | ; Param - an arbitrary pointer accessible within the funtion with sqlite3_user_data() 1015 | ; Return values: On success - True 1016 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 1017 | ; Documentation: www.sqlite.org/c3ref/create_function.html 1018 | ; =================================================================================================================== 1019 | CreateScalarFunc(Name, Args, Func, Enc := 0x0801, Param := 0) { 1020 | ; SQLITE_DETERMINISTIC = 0x0800 - the function will always return the same result given the same inputs 1021 | ; within a single SQL statement 1022 | ; SQLITE_UTF8 = 0x0001 1023 | This.ErrorMsg := "" 1024 | This.ErrorCode := 0 1025 | If !(This._Handle) { 1026 | This.ErrorMsg := "Invalid database handle!" 1027 | Return False 1028 | } 1029 | RC := DllCall("SQLite3.dll\sqlite3_create_function", "Ptr", This._Handle, "AStr", Name, "Int", Args, "Int", Enc 1030 | , "Ptr", Param, "Ptr", Func, "Ptr", 0, "Ptr", 0, "Cdecl Int") 1031 | If (ErrorLeveL) { 1032 | This.ErrorMsg := "DllCall sqlite3_create_function failed!" 1033 | This.ErrorCode := ErrorLevel 1034 | Return False 1035 | } 1036 | If (RC) { 1037 | This.ErrorMsg := This._ErrMsg() 1038 | This.ErrorCode := RC 1039 | Return False 1040 | } 1041 | Return True 1042 | } 1043 | ; =================================================================================================================== 1044 | ; METHOD LastInsertRowID Get the ROWID of the last inserted row 1045 | ; Parameters: ByRef RowID - Variable to store the ROWID 1046 | ; Return values: On success - True, RowID contains the ROWID 1047 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 1048 | ; =================================================================================================================== 1049 | LastInsertRowID(ByRef RowID) { 1050 | This.ErrorMsg := "" 1051 | This.ErrorCode := 0 1052 | This.SQL := "" 1053 | If !(This._Handle) { 1054 | This.ErrorMsg := "Invalid database handle!" 1055 | Return False 1056 | } 1057 | RowID := 0 1058 | RC := DllCall("SQLite3.dll\sqlite3_last_insert_rowid", "Ptr", This._Handle, "Cdecl Int64") 1059 | If (ErrorLevel) { 1060 | This.ErrorMsg := "DllCall sqlite3_last_insert_rowid failed!" 1061 | This.ErrorCode := ErrorLevel 1062 | Return False 1063 | } 1064 | RowID := RC 1065 | Return True 1066 | } 1067 | ; =================================================================================================================== 1068 | ; METHOD TotalChanges Get the number of changed rows since connecting to the database 1069 | ; Parameters: ByRef Rows - Variable to store the number of rows 1070 | ; Return values: On success - True, Rows contains the number of rows 1071 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 1072 | ; =================================================================================================================== 1073 | TotalChanges(ByRef Rows) { 1074 | This.ErrorMsg := "" 1075 | This.ErrorCode := 0 1076 | This.SQL := "" 1077 | If !(This._Handle) { 1078 | This.ErrorMsg := "Invalid database handle!" 1079 | Return False 1080 | } 1081 | Rows := 0 1082 | RC := DllCall("SQLite3.dll\sqlite3_total_changes", "Ptr", This._Handle, "Cdecl Int") 1083 | If (ErrorLevel) { 1084 | This.ErrorMsg := "DllCall sqlite3_total_changes failed!" 1085 | This.ErrorCode := ErrorLevel 1086 | Return False 1087 | } 1088 | Rows := RC 1089 | Return True 1090 | } 1091 | ; =================================================================================================================== 1092 | ; METHOD SetTimeout Set the timeout to wait before SQLITE_BUSY or SQLITE_IOERR_BLOCKED is returned, 1093 | ; when a table is locked. 1094 | ; Parameters: TimeOut - Time to wait in milliseconds 1095 | ; Return values: On success - True 1096 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 1097 | ; =================================================================================================================== 1098 | SetTimeout(Timeout := 1000) { 1099 | This.ErrorMsg := "" 1100 | This.ErrorCode := 0 1101 | This.SQL := "" 1102 | If !(This._Handle) { 1103 | This.ErrorMsg := "Invalid database handle!" 1104 | Return False 1105 | } 1106 | If Timeout Is Not Integer 1107 | Timeout := 1000 1108 | RC := DllCall("SQLite3.dll\sqlite3_busy_timeout", "Ptr", This._Handle, "Int", Timeout, "Cdecl Int") 1109 | If (ErrorLevel) { 1110 | This.ErrorMsg := "DllCall sqlite3_busy_timeout failed!" 1111 | This.ErrorCode := ErrorLevel 1112 | Return False 1113 | } 1114 | If (RC) { 1115 | This.ErrorMsg := This._ErrMsg() 1116 | This.ErrorCode := RC 1117 | Return False 1118 | } 1119 | Return True 1120 | } 1121 | ; =================================================================================================================== 1122 | ; METHOD EscapeStr Escapes special characters in a string to be used as field content 1123 | ; Parameters: Str - String to be escaped 1124 | ; Quote - Add single quotes around the outside of the total string (True / False) 1125 | ; Return values: On success - True 1126 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 1127 | ; =================================================================================================================== 1128 | EscapeStr(ByRef Str, Quote := True) { 1129 | This.ErrorMsg := "" 1130 | This.ErrorCode := 0 1131 | This.SQL := "" 1132 | If !(This._Handle) { 1133 | This.ErrorMsg := "Invalid database handle!" 1134 | Return False 1135 | } 1136 | If Str Is Number 1137 | Return True 1138 | VarSetCapacity(OP, 16, 0) 1139 | StrPut(Quote ? "%Q" : "%q", &OP, "UTF-8") 1140 | This._StrToUTF8(Str, UTF8) 1141 | Ptr := DllCall("SQLite3.dll\sqlite3_mprintf", "Ptr", &OP, "Ptr", &UTF8, "Cdecl UPtr") 1142 | If (ErrorLevel) { 1143 | This.ErrorMsg := "DllCall sqlite3_mprintf failed!" 1144 | This.ErrorCode := ErrorLevel 1145 | Return False 1146 | } 1147 | Str := This._UTF8ToStr(Ptr) 1148 | DllCall("SQLite3.dll\sqlite3_free", "Ptr", Ptr, "Cdecl") 1149 | Return True 1150 | } 1151 | ; =================================================================================================================== 1152 | ; METHOD StoreBLOB Use BLOBs as parameters of an INSERT/UPDATE/REPLACE statement. 1153 | ; Parameters: SQL - SQL statement to be compiled 1154 | ; BlobArray - Array of objects containing two keys/value pairs: 1155 | ; Addr : Address of the (variable containing the) BLOB. 1156 | ; Size : Size of the BLOB in bytes. 1157 | ; Return values: On success - True 1158 | ; On failure - False, ErrorMsg / ErrorCode contain additional information 1159 | ; Remarks: For each BLOB in the row you have to specify a ? parameter within the statement. The 1160 | ; parameters are numbered automatically from left to right starting with 1. 1161 | ; For each parameter you have to pass an object within BlobArray containing the address 1162 | ; and the size of the BLOB. 1163 | ; =================================================================================================================== 1164 | StoreBLOB(SQL, BlobArray) { 1165 | Static SQLITE_STATIC := 0 1166 | Static SQLITE_TRANSIENT := -1 1167 | This.ErrorMsg := "" 1168 | This.ErrorCode := 0 1169 | If !(This._Handle) { 1170 | This.ErrorMsg := "Invalid database handle!" 1171 | Return False 1172 | } 1173 | If !RegExMatch(SQL, "i)^\s*(INSERT|UPDATE|REPLACE)\s") { 1174 | This.ErrorMsg := A_ThisFunc . " requires an INSERT/UPDATE/REPLACE statement!" 1175 | Return False 1176 | } 1177 | Query := 0 1178 | This._StrToUTF8(SQL, UTF8) 1179 | RC := DllCall("SQlite3.dll\sqlite3_prepare_v2", "Ptr", This._Handle, "Ptr", &UTF8, "Int", -1 1180 | , "UPtrP", Query, "Ptr", 0, "Cdecl Int") 1181 | If (ErrorLeveL) { 1182 | This.ErrorMsg := A_ThisFunc . ": DllCall sqlite3_prepare_v2 failed!" 1183 | This.ErrorCode := ErrorLevel 1184 | Return False 1185 | } 1186 | If (RC) { 1187 | This.ErrorMsg := A_ThisFunc . ": " . This._ErrMsg() 1188 | This.ErrorCode := RC 1189 | Return False 1190 | } 1191 | For BlobNum, Blob In BlobArray { 1192 | If !(Blob.Addr) || !(Blob.Size) { 1193 | This.ErrorMsg := A_ThisFunc . ": Invalid parameter BlobArray!" 1194 | This.ErrorCode := ErrorLevel 1195 | Return False 1196 | } 1197 | RC := DllCall("SQlite3.dll\sqlite3_bind_blob", "Ptr", Query, "Int", BlobNum, "Ptr", Blob.Addr 1198 | , "Int", Blob.Size, "Ptr", SQLITE_STATIC, "Cdecl Int") 1199 | If (ErrorLeveL) { 1200 | This.ErrorMsg := A_ThisFunc . ": DllCall sqlite3_bind_blob failed!" 1201 | This.ErrorCode := ErrorLevel 1202 | Return False 1203 | } 1204 | If (RC) { 1205 | This.ErrorMsg := A_ThisFunc . ": " . This._ErrMsg() 1206 | This.ErrorCode := RC 1207 | Return False 1208 | } 1209 | } 1210 | RC := DllCall("SQlite3.dll\sqlite3_step", "Ptr", Query, "Cdecl Int") 1211 | If (ErrorLevel) { 1212 | This.ErrorMsg := A_ThisFunc . ": DllCall sqlite3_step failed!" 1213 | This.ErrorCode := ErrorLevel 1214 | Return False 1215 | } 1216 | If (RC) && (RC <> This._ReturnCode("SQLITE_DONE")) { 1217 | This.ErrorMsg := A_ThisFunc . ": " . This._ErrMsg() 1218 | This.ErrorCode := RC 1219 | Return False 1220 | } 1221 | RC := DllCall("SQlite3.dll\sqlite3_finalize", "Ptr", Query, "Cdecl Int") 1222 | If (ErrorLevel) { 1223 | This.ErrorMsg := A_ThisFunc . ": DllCall sqlite3_finalize failed!" 1224 | This.ErrorCode := ErrorLevel 1225 | Return False 1226 | } 1227 | If (RC) { 1228 | This.ErrorMsg := A_ThisFunc . ": " . This._ErrMsg() 1229 | This.ErrorCode := RC 1230 | Return False 1231 | } 1232 | Return True 1233 | } 1234 | } 1235 | ; ====================================================================================================================== 1236 | ; Exemplary custom callback function regexp() 1237 | ; Parameters: Context - handle to a sqlite3_context object 1238 | ; ArgC - number of elements passed in Values (must be 2 for this function) 1239 | ; Values - pointer to an array of pointers which can be passed to sqlite3_value_text(): 1240 | ; 1. Needle 1241 | ; 2. Haystack 1242 | ; Return values: Call sqlite3_result_int() passing 1 (True) for a match, otherwise pass 0 (False). 1243 | ; ====================================================================================================================== 1244 | SQLiteDB_RegExp(Context, ArgC, Values) { 1245 | Result := 0 1246 | If (ArgC = 2) { 1247 | AddrN := DllCall("SQLite3.dll\sqlite3_value_text", "Ptr", NumGet(Values + 0, "UPtr"), "Cdecl UPtr") 1248 | AddrH := DllCall("SQLite3.dll\sqlite3_value_text", "Ptr", NumGet(Values + A_PtrSize, "UPtr"), "Cdecl UPtr") 1249 | Result := RegExMatch(StrGet(AddrH, "UTF-8"), StrGet(AddrN, "UTF-8")) 1250 | } 1251 | DllCall("SQLite3.dll\sqlite3_result_int", "Ptr", Context, "Int", !!Result, "Cdecl") ; 0 = false, 1 = trus 1252 | } 1253 | -------------------------------------------------------------------------------- /Sources_v1.1/SQLiteDB_sample.ahk: -------------------------------------------------------------------------------- 1 | ; ====================================================================================================================== 2 | ; Script Function: Sample script for Class_SQLiteDB.ahk 3 | ; AHK Version: L 1.1.00.00 (U 32) 4 | ; Language: English 5 | ; Tested on: Win XPSP3, Win VistaSP2 (32 Bit) 6 | ; Author: just me 7 | ; Version: 0.0.00.03/2011-05-24/just me 8 | ; ====================================================================================================================== 9 | ; AHK Settings 10 | ; ====================================================================================================================== 11 | #NoEnv 12 | ; #Warn 13 | #SingleInstance force 14 | SetWorkingDir, %A_ScriptDir% 15 | SetBatchLines, -1 16 | OnExit, GuiClose 17 | ; ====================================================================================================================== 18 | ; Includes 19 | ; ====================================================================================================================== 20 | #Include Class_SQLiteDB.ahk 21 | ; ====================================================================================================================== 22 | ; Start & GUI 23 | ; ====================================================================================================================== 24 | CBBSQL := "SELECT * FROM Test" 25 | DBFileName := A_ScriptDir . "\TEST.DB" 26 | Title := "SQL Query/Command ListView Function GUI" 27 | If FileExist(DBFileName) { 28 | SB_SetText("Deleting " . DBFileName) 29 | FileDelete, %DBFileName% 30 | } 31 | Gui, +LastFound +OwnDialogs +Disabled 32 | Gui, Margin, 10, 10 33 | Gui, Add, Text, w100 h20 0x200 vTX, SQL statement: 34 | Gui, Add, ComboBox, x+0 ym w590 vSQL Sort, %CBBSQL% 35 | GuiControlGet, P, Pos, SQL 36 | GuiControl, Move, TX, h%PH% 37 | Gui, Add, Button, ym w80 hp vRun gRunSQL Default, Run 38 | Gui, Add, Text, xm h20 w100 0x200, Table name: 39 | Gui, Add, Edit, x+0 yp w150 hp vTable, Test 40 | Gui, Add, Button, Section x+10 yp wp hp gGetTable, Get _Table 41 | Gui, Add, Button, x+10 yp wp hp gGetRecordSet, Get _RecordSet 42 | Gui, Add, GroupBox, xm w780 h330, Results 43 | Gui, Add, ListView, xp+10 yp+18 w760 h300 vResultsLV +LV0x00010000 44 | Gui, Add, StatusBar, 45 | Gui, Show, , %Title% 46 | ; ====================================================================================================================== 47 | ; Use Class SQLiteDB : Initialize and get lib version 48 | ; ====================================================================================================================== 49 | SB_SetText("SQLiteDB new") 50 | DB := new SQLiteDB 51 | Sleep, 1000 52 | SB_SetText("Version") 53 | Version := DB.Version 54 | WinSetTitle, %Title% - SQLite3.dll v %Version% 55 | Sleep, 1000 56 | ; ====================================================================================================================== 57 | ; Use Class SQLiteDB : Open/Create database and table 58 | ; ====================================================================================================================== 59 | SB_SetText("OpenDB") 60 | If !DB.OpenDB(DBFileName) { 61 | MsgBox, 16, SQLite Error, % "Msg:`t" . DB.ErrorMsg . "`nCode:`t" . DB.ErrorCode 62 | ExitApp 63 | } 64 | Sleep, 1000 65 | SB_SetText("Exec: CREATE TABLE") 66 | SQL := "CREATE TABLE Test (Name, Fname, Phone, Room, PRIMARY KEY(Name ASC, FName ASC));" 67 | If !DB.Exec(SQL) 68 | MsgBox, 16, SQLite Error, % "Msg:`t" . DB.ErrorMsg . "`nCode:`t" . DB.ErrorCode 69 | Sleep, 1000 70 | SB_SetText("Exec: INSERT 1000 rows") 71 | Start := A_TickCount 72 | DB.Exec("BEGIN TRANSACTION;") 73 | SQLStr := "" 74 | _SQL := "INSERT INTO Test VALUES('Näme#', 'Fname#', 'Phone#', 'Room#');" 75 | Loop, 1000 { 76 | StringReplace, SQL, _SQL, #, %A_Index%, All 77 | SQLStr .= SQL 78 | } 79 | If !DB.Exec(SQLStr) 80 | MsgBox, 16, SQLite Error, % "Msg:`t" . DB.ErrorMsg . "`nCode:`t" . DB.ErrorCode 81 | DB.Exec("COMMIT TRANSACTION;") 82 | SQLStr := "" 83 | SB_SetText("Exec: INSERT 1000 rows done in " . (A_TickCount - Start) . " ms") 84 | Sleep, 1000 85 | ; ====================================================================================================================== 86 | ; Use Class SQLiteDB : Using Exec() with callback function 87 | ; ====================================================================================================================== 88 | SB_SetText("Exec: Using a callback function") 89 | SQL := "SELECT COUNT(*) FROM Test;" 90 | DB.Exec(SQL, "SQLiteExecCallBack") 91 | ; ====================================================================================================================== 92 | ; Use Class SQLiteDB : Get some informations 93 | ; ====================================================================================================================== 94 | SB_SetText("LastInsertRowID") 95 | If !DB.LastInsertRowID(RowID) 96 | MsgBox, 16, SQLite Error, % "Msg:`t" . DB.ErrorMsg . "`nCode:`t" . DB.ErrorCode 97 | GuiControl, -ReDraw, ResultsLV 98 | LV_Delete() 99 | ColCount := LV_GetCount("Column") 100 | Loop, %ColCount% 101 | LV_DeleteCol(1) 102 | LV_InsertCol(1,"", "LastInsertedRowID") 103 | LV_Add("", RowID) 104 | GuiControl, +ReDraw, ResultsLV 105 | Sleep, 1000 106 | SQL := "SELECT COUNT(*) FROM Test;" 107 | SB_SetText("SQLite_GetTable : " . SQL) 108 | Result := "" 109 | If !DB.GetTable(SQL, Result) 110 | MsgBox, 16, SQLite Error: GetTable, % "Msg:`t" . DB.ErrorMsg . "`nCode:`t" . DB.ErrorCode 111 | ShowTable(Result) 112 | Sleep, 1000 113 | ; ====================================================================================================================== 114 | ; Start of query using Query() : Get the column names for table Test 115 | ; ====================================================================================================================== 116 | SQL := "SELECT * FROM Test;" 117 | SB_SetText("Query : " . SQL) 118 | If !DB.Query(SQL, RecordSet) 119 | MsgBox, 16, SQLite Error: Query, % "Msg:`t" . RecordSet.ErrorMsg . "`nCode:`t" . RecordSet.ErrorCode 120 | GuiControl, -ReDraw, ResultsLV 121 | LV_Delete() 122 | ColCount := LV_GetCount("Column") 123 | Loop, %ColCount% 124 | LV_DeleteCol(1) 125 | LV_InsertCol(1,"", "Column names") 126 | Loop, % RecordSet.ColumnCount 127 | LV_Add("", RecordSet.ColumnNames[A_Index]) 128 | LV_ModifyCol(1, "AutoHdr") 129 | RecordSet.Free() 130 | GuiControl, +ReDraw, ResultsLV 131 | ; ====================================================================================================================== 132 | ; End of query using Query() 133 | ; ====================================================================================================================== 134 | Gui, -Disabled 135 | Return 136 | ; ====================================================================================================================== 137 | ; Gui Subs 138 | ; ====================================================================================================================== 139 | GuiClose: 140 | GuiEscape: 141 | If !DB.CloseDB() 142 | MsgBox, 16, SQLite Error, % "Msg:`t" . DB.ErrorMsg . "`nCode:`t" . DB.ErrorCode 143 | Gui, Destroy 144 | ExitApp 145 | ; ====================================================================================================================== 146 | ; Other Subs 147 | ; ====================================================================================================================== 148 | ; "One step" query using GetTable() 149 | ; ====================================================================================================================== 150 | GetTable: 151 | Gui, Submit, NoHide 152 | Result := "" 153 | SQL := "SELECT * FROM " . Table . ";" 154 | SB_SetText("GetTable: " . SQL) 155 | Start := A_TickCount 156 | If !DB.GetTable(SQL, Result) 157 | MsgBox, 16, SQLite Error, % "Msg:`t" . DB.ErrorMsg . "`nCode:`t" . DB.ErrorCode 158 | SB_SetText("GetTable: " . SQL . " done in " . (A_TickCount - Start) . " ms") 159 | ShowTable(Result) 160 | Return 161 | ; ====================================================================================================================== 162 | ; Show results for prepared query using Query() 163 | ; ====================================================================================================================== 164 | GetRecordSet: 165 | Gui, Submit, NoHide 166 | SQL := "SELECT * FROM " . Table . ";" 167 | SB_SetText("Query: " . SQL) 168 | RecordSet := "" 169 | Start := A_TickCount 170 | If !DB.Query(SQL, RecordSet) 171 | MsgBox, 16, SQLite Error: Query, % "Msg:`t" . RecordSet.ErrorMsg . "`nCode:`t" . RecordSet.ErrorCode 172 | ShowRecordSet(RecordSet) 173 | RecordSet.Free() 174 | SB_SetText("Query: " . SQL . " done in " . (A_TickCount - Start) . " ms") 175 | Return 176 | ; ====================================================================================================================== 177 | ; Execute SQL statement using Exec() / GetTable() 178 | ; ====================================================================================================================== 179 | RunSQL: 180 | Gui, +OwnDialogs 181 | GuiControlGet, SQL 182 | If SQL Is Space 183 | { 184 | SB_SetText("No text entered") 185 | Return 186 | } 187 | If !InStr("`n" . CBBSQL . "`n", "`n" . SQL . "`n") { 188 | GuiControl, , SQL, %SQL% 189 | CBBSQL .= "`n" . SQL 190 | } 191 | If (SubStr(SQL, 0) <> ";") 192 | SQL .= ";" 193 | Result := "" 194 | If RegExMatch(SQL, "i)^\s*SELECT\s") { 195 | SB_SetText("GetTable: " . SQL) 196 | If !DB.GetTable(SQL, Result) 197 | MsgBox, 16, SQLite Error: GetTable, % "Msg:`t" . DB.ErrorMsg . "`nCode:`t" . DB.ErrorCode 198 | Else 199 | ShowTable(Result) 200 | SB_SetText("GetTable: " . SQL . " done!") 201 | } Else { 202 | SB_SetText("Exec: " . SQL) 203 | If !DB.Exec(SQL) 204 | MsgBox, 16, SQLite Error: Exec, % "Msg:`t" . DB.ErrorMsg . "`nCode:`t" . DB.ErrorCode 205 | Else 206 | SB_SetText("Exec: " . SQL . " done!") 207 | } 208 | Return 209 | ; ====================================================================================================================== 210 | ; Exec() callback function sample 211 | ; ====================================================================================================================== 212 | SQLiteExecCallBack(DB, ColumnCount, ColumnValues, ColumnNames) { 213 | This := Object(DB) 214 | MsgBox, 0, %A_ThisFunc% 215 | , % "SQLite version: " . This.Version . "`n" 216 | . "SQL statement: " . StrGet(A_EventInfo) . "`n" 217 | . "Number of columns: " . ColumnCount . "`n" 218 | . "Name of first column: " . StrGet(NumGet(ColumnNames + 0, "UInt"), "UTF-8") . "`n" 219 | . "Value of first column: " . StrGet(NumGet(ColumnValues + 0, "UInt"), "UTF-8") 220 | Return 0 221 | } 222 | ; ====================================================================================================================== 223 | ; Show results 224 | ; ====================================================================================================================== 225 | ShowTable(Table) { 226 | Global 227 | Local ColCount, RowCount, Row 228 | GuiControl, -ReDraw, ResultsLV 229 | LV_Delete() 230 | ColCount := LV_GetCount("Column") 231 | Loop, %ColCount% 232 | LV_DeleteCol(1) 233 | If (Table.HasNames) { 234 | Loop, % Table.ColumnCount 235 | LV_InsertCol(A_Index,"", Table.ColumnNames[A_Index]) 236 | If (Table.HasRows) { 237 | Loop, % Table.RowCount { 238 | RowCount := LV_Add("", "") 239 | Table.Next(Row) 240 | Loop, % Table.ColumnCount 241 | LV_Modify(RowCount, "Col" . A_Index, Row[A_Index]) 242 | } 243 | } 244 | Loop, % Table.ColumnCount 245 | LV_ModifyCol(A_Index, "AutoHdr") 246 | } 247 | GuiControl, +ReDraw, ResultsLV 248 | } 249 | ; ---------------------------------------------------------------------------------------------------------------------- 250 | ShowRecordSet(RecordSet) { 251 | Global 252 | Local ColCount, RowCount, Row, RC 253 | GuiControl, -ReDraw, ResultsLV 254 | LV_Delete() 255 | ColCount := LV_GetCount("Column") 256 | Loop, %ColCount% 257 | LV_DeleteCol(1) 258 | If (RecordSet.HasNames) { 259 | Loop, % RecordSet.ColumnCount 260 | LV_InsertCol(A_Index,"", RecordSet.ColumnNames[A_Index]) 261 | } 262 | If (RecordSet.HasRows) { 263 | If (RecordSet.Next(Row) < 1) { 264 | MsgBox, 16, %A_ThisFunc%, % "Msg:`t" . RecordSet.ErrorMsg . "`nCode:`t" . RecordSet.ErrorCode 265 | Return 266 | } 267 | Loop { 268 | RowCount := LV_Add("", "") 269 | Loop, % RecordSet.ColumnCount 270 | LV_Modify(RowCount, "Col" . A_Index, Row[A_Index]) 271 | RC := RecordSet.Next(Row) 272 | } Until (RC < 1) 273 | } 274 | If (RC = 0) 275 | MsgBox, 16, %A_ThisFunc%, % "Msg:`t" . RecordSet.ErrorMsg . "`nCode:`t" . RecordSet.ErrorCode 276 | Loop, % RecordSet.ColumnCount 277 | LV_ModifyCol(A_Index, "AutoHdr") 278 | GuiControl, +ReDraw, ResultsLV 279 | } --------------------------------------------------------------------------------