diff --git a/Clipjump.ahk b/Clipjump.ahk index ed5f5bb..8291103 100644 --- a/Clipjump.ahk +++ b/Clipjump.ahk @@ -34,6 +34,10 @@ ListLines, Off #KeyHistory 0 #HotkeyInterval 1000 #MaxHotkeysPerInterval 1000 +#Persistent ; Prevent the script from exiting automatically. +DetectHiddenWindows, on + +OnExit("ExitFunc") global ini_LANG := "" , H_Compiled := RegexMatch(Substr(A_AhkPath, Instr(A_AhkPath, "\", 0, 0)+1), "iU)^(Clipjump).*(\.exe)$") && (!A_IsCompiled) ? 1 : 0 global mainIconPath := H_Compiled || A_IsCompiled ? A_AhkPath : "icons/icon.ico" @@ -1339,11 +1343,14 @@ copyFile: try Clipboard := selectedfile CopyMessage := MSG_TRANSFER_COMPLETE " {" CN.Name "}" return + +; convert file path \ to / to make it usable in R copyFolder: copyMessage := MSG_FOLDER_PATH_COPIED openedFolder := GetFolder() if ( openedfolder != "" ) + StringReplace, openedFolder, openedFolder, \, /, All try Clipboard := openedFolder copyMessage := MSG_TRANSFER_COMPLETE " {" CN.Name "}" return @@ -1661,4 +1668,14 @@ Receive_WM_COPYDATA(wParam, lParam){ #include %A_ScriptDir%\lib\settingsHelper.ahk #include *i %A_ScriptDir%\plugins\_registry.ahk -;------------------------------------------------------------------- X ------------------------------------------------------------------------------- \ No newline at end of file +;------------------------------------------------------------------- X ------------------------------------------------------------------------------- + +;Force close function to unload Clipjump on exit. Requires Nircmd + +ExitFunc() +{ + + WinGet pid, PID, %A_ScriptFullPath% + Process, Close, %pid% + ; Do not call ExitApp -- that would prevent other OnExit functions from being called. +} diff --git a/Clipjump_x64.ahk b/Clipjump_x64.ahk index d808ce7..0d1acf5 100644 --- a/Clipjump_x64.ahk +++ b/Clipjump_x64.ahk @@ -1,3 +1,2 @@ #Notrayicon - -run, %A_AhkPath% "%A_ScriptDir%\Clipjump.ahk" \ No newline at end of file +run, *RunAs %A_AhkPath% "%A_ScriptDir%\Clipjump.ahk" \ No newline at end of file diff --git a/Readme.md b/Readme.md index 9582399..b0eca83 100644 --- a/Readme.md +++ b/Readme.md @@ -1,6 +1,6 @@ ## Clipjump -[Download](https://github.com/aviaryan/Clipjump/releases) +[Download](https://github.com/sobuj53/Clipjump/releases) [Online Manual](http://clipjump.sourceforge.net/docs/) Clipjump is a Multiple-Clipboard management utility for Windows. @@ -27,4 +27,4 @@ Docs can be compiled using Jekyll and then Microsoft's HHC. First build the webs #### Adding Plugins Please add your plugins to the [clipjump-addons](https://github.com/aviaryan/clipjump-addons) repository. This repo only contains plugins distributed with official Clipjump release. -If I realise your plugin is useful for the community, I will distribute it officially and hence it will be added to this repository. See [#68](https://github.com/aviaryan/Clipjump/issues/68) \ No newline at end of file +If I realise your plugin is useful for the community, I will distribute it officially and hence it will be added to this repository. See [#68](https://github.com/aviaryan/Clipjump/issues/68) diff --git a/lib/History GUI Plug.ahk b/lib/History GUI Plug.ahk index 0abdbb2..d3bbae4 100644 --- a/lib/History GUI Plug.ahk +++ b/lib/History GUI Plug.ahk @@ -672,7 +672,9 @@ addHistoryImage(imgpath, timestamp){ ; timestamp is in A_Now format timestamp := convertTimeSql(timestamp) fptr := FileOpen(imgpath, "r") - size := fptr.Length + if fptr.Length + size := fptr.Length + else size := 0 fptr.Close() while (FileExist(rname := "cache\history\" getRandomStr(15) ".jpg")){ ; loop till new file name diff --git a/lib/SQLiteDB/Class_SQLiteDB.ahk b/lib/SQLiteDB/Class_SQLiteDB.ahk index 3ce2c2b..0a84cdb 100644 --- a/lib/SQLiteDB/Class_SQLiteDB.ahk +++ b/lib/SQLiteDB/Class_SQLiteDB.ahk @@ -1,12 +1,17 @@ ; ====================================================================================================================== ; Function: Class definitions as wrappers for SQLite3.dll to work with SQLite DBs. -; AHK version: v2 alpha -; Tested on: Win 7 Pro (64 Bit), SQLite 3.7.6 +; AHK version: 1.1.23.01 +; Tested on: Win 10 Pro (x64), SQLite 3.7.13 ; Version: 0.0.01.00/2011-08-10/just me ; 0.0.02.00/2012-08-10/just me - Added basic BLOB support ; 0.0.03.00/2012-08-11/just me - Added more advanced BLOB support ; 0.0.04.00/2013-06-29/just me - Added new methods AttachDB and DetachDB ; 0.0.05.00/2013-08-03/just me - Changed base class assignment +; 0.0.06.00/2016-01-28/just me - Fixed version check, revised parameter initialization. +; 0.0.07.00/2016-03-28/just me - Added support for PRAGMA statements. +; 0.0.08.00/2019-03-09/just me - Added basic support for application-defined functions +; 0.0.09.00/2019-07-09/just me - Added basic support for prepared statements, minor bug fixes +; 0.0.10.00/2019-12-12/just me - Fixed bug in EscapeStr method ; Remarks: Names of "private" properties / methods are prefixed with an underscore, ; they must not be set / called by the script! ; @@ -25,21 +30,16 @@ ; ====================================================================================================================== ; CLASS SQliteDB - SQLiteDB main class ; ====================================================================================================================== -Class SQLiteDB Extends SQLiteDB.BaseClass { +Class SQLiteDB { ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; PRIVATE Properties and Methods ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - ; =================================================================================================================== - ; BaseClass - SQLiteDB base class - ; =================================================================================================================== - Class BaseClass { - Static Version := "" - Static _SQLiteDLL := A_ScriptDir . "\SQLite3.dll" - Static _RefCount := 0 - Static _MinVersion := 36 - } + Static Version := "" + Static _SQLiteDLL := A_ScriptDir . "\SQLite3.dll" + Static _RefCount := 0 + Static _MinVersion := "3.6" ; =================================================================================================================== ; CLASS _Table ; Object returned from method GetTable() @@ -50,7 +50,7 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { ; CONSTRUCTOR Create instance variables ; ---------------------------------------------------------------------------------------------------------------- __New() { - This.ColumnCount := 0 ; Number of coumns in the result table (Integer) + This.ColumnCount := 0 ; Number of columns in the result table (Integer) This.RowCount := 0 ; Number of rows in the result table (Integer) This.ColumnNames := [] ; Names of columns in the result table (Array) This.Rows := [] ; Rows of the result table (Array of Arrays) @@ -116,15 +116,22 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { ; CONSTRUCTOR Create instance variables ; ---------------------------------------------------------------------------------------------------------------- __New() { - This.ColumnCount := 0 ; Number of columns (Integer) - This.ColumnNames := [] ; Names of columns in the result table (Array) - This.HasNames := False ; Does var ColumnNames contain names? (Bool) - This.HasRows := False ; Does _RecordSet contain rows? (Bool) - This.CurrentRow := 0 ; Index of current row (Integer) - This.ErrorMsg := "" ; Last error message (String) - This.ErrorCode := 0 ; Last SQLite error code / ErrorLevel (Variant) - This._Handle := 0 ; Query handle (Pointer) - This._DB := {} ; SQLiteDB object (Object) + This.ColumnCount := 0 ; Number of columns (Integer) + This.ColumnNames := [] ; Names of columns in the result table (Array) + This.HasNames := False ; Does var ColumnNames contain names? (Bool) + This.HasRows := False ; Does _RecordSet contain rows? (Bool) + This.CurrentRow := 0 ; Index of current row (Integer) + This.ErrorMsg := "" ; Last error message (String) + This.ErrorCode := 0 ; Last SQLite error code / ErrorLevel (Variant) + This._Handle := 0 ; Query handle (Pointer) + This._DB := {} ; SQLiteDB object (Object) + } + ; ---------------------------------------------------------------------------------------------------------------- + ; DESTRUCTOR Clear instance variables + ; ---------------------------------------------------------------------------------------------------------------- + __Delete() { + If (This._Handle) + This.Free() } ; ---------------------------------------------------------------------------------------------------------------- ; METHOD Next Get next row of query result @@ -146,7 +153,7 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { } RC := DllCall("SQlite3.dll\sqlite3_step", "Ptr", This._Handle, "Cdecl Int") If (ErrorLevel) { - This.ErrorMsg := "DLLCall sqlite3_step failed!" + This.ErrorMsg := "DllCall sqlite3_step failed!" This.ErrorCode := ErrorLevel Return False } @@ -162,12 +169,12 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { } RC := DllCall("SQlite3.dll\sqlite3_data_count", "Ptr", This._Handle, "Cdecl Int") If (ErrorLevel) { - This.ErrorMsg := "DLLCall sqlite3_data_count failed!" + This.ErrorMsg := "DllCall sqlite3_data_count failed!" This.ErrorCode := ErrorLevel Return False } If (RC < 1) { - This.ErrorMsg := "RecordSet is empty!" + This.ErrorMsg := "Recordset is empty!" This.ErrorCode := This._DB._ReturnCode("SQLITE_EMPTY") Return False } @@ -176,7 +183,7 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { Column := A_Index - 1 ColumnType := DllCall("SQlite3.dll\sqlite3_column_type", "Ptr", This._Handle, "Int", Column, "Cdecl Int") If (ErrorLevel) { - This.ErrorMsg := "DLLCall sqlite3_column_type failed!" + This.ErrorMsg := "DllCall sqlite3_column_type failed!" This.ErrorCode := ErrorLevel Return False } @@ -198,7 +205,7 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { } Else { StrPtr := DllCall("SQlite3.dll\sqlite3_column_text", "Ptr", This._Handle, "Int", Column, "Cdecl UPtr") If (ErrorLevel) { - This.ErrorMsg := "DLLCall sqlite3_column_text failed!" + This.ErrorMsg := "DllCall sqlite3_column_text failed!" This.ErrorCode := ErrorLevel Return False } @@ -224,7 +231,7 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { } RC := DllCall("SQlite3.dll\sqlite3_reset", "Ptr", This._Handle, "Cdecl Int") If (ErrorLevel) { - This.ErrorMsg := "DLLCall sqlite3_reset failed!" + This.ErrorMsg := "DllCall sqlite3_reset failed!" This.ErrorCode := ErrorLevel Return False } @@ -250,7 +257,247 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { Return True RC := DllCall("SQlite3.dll\sqlite3_finalize", "Ptr", This._Handle, "Cdecl Int") If (ErrorLevel) { - This.ErrorMsg := "DLLCall sqlite3_finalize failed!" + This.ErrorMsg := "DllCall sqlite3_finalize failed!" + This.ErrorCode := ErrorLevel + Return False + } + If (RC) { + This.ErrorMsg := This._DB._ErrMsg() + This.ErrorCode := RC + Return False + } + This._DB._Queries.Delete(This._Handle) + This._Handle := 0 + This._DB := 0 + Return True + } + } + ; =================================================================================================================== + ; CLASS _Statement + ; Object returned from method Prepare() + ; The life-cycle of a prepared statement object usually goes like this: + ; 1. Create the prepared statement object (PST) by calling DB.Prepare(). + ; 2. Bind values to parameters using the PST.Bind_*() methods of the statement object. + ; 3. Run the SQL by calling PST.Step() one or more times. + ; 4. Reset the prepared statement using PTS.Reset() then go back to step 2. Do this zero or more times. + ; 5. Destroy the object using PST.Finalize(). + ; The lifetime of a prepared statement depends on the lifetime of the related SQLiteDB object. + ; =================================================================================================================== + Class _Statement { + ; ---------------------------------------------------------------------------------------------------------------- + ; CONSTRUCTOR Create instance variables + ; ---------------------------------------------------------------------------------------------------------------- + __New() { + This.ErrorMsg := "" ; Last error message (String) + This.ErrorCode := 0 ; Last SQLite error code / ErrorLevel (Variant) + This.ParamCount := 0 ; Number of SQL parameters for this statement (Integer) + This._Handle := 0 ; Query handle (Pointer) + This._DB := {} ; SQLiteDB object (Object) + } + ; ---------------------------------------------------------------------------------------------------------------- + ; DESTRUCTOR Clear instance variables + ; ---------------------------------------------------------------------------------------------------------------- + __Delete() { + If (This._Handle) + This.Free() + } + ; ---------------------------------------------------------------------------------------------------------------- + ; METHOD Bind Bind values to SQL parameters. + ; Parameters: Index - 1-based index of the SQL parameter + ; Type - type of the SQL parameter (currently: Blob/Double/Int/Text) + ; Param3 - type dependent value + ; Param4 - type dependent value + ; Param5 - not used + ; Return values: On success - True + ; On failure - False, ErrorMsg / ErrorCode contain additional information + ; ---------------------------------------------------------------------------------------------------------------- + Bind(Index, Type, Param3 := "", Param4 := 0, Param5 := 0) { + Static SQLITE_STATIC := 0 + Static SQLITE_TRANSIENT := -1 + Static Types := {Blob: 1, Double: 1, Int: 1, Text: 1} + This.ErrorMsg := "" + This.ErrorCode := 0 + If !(This._Handle) { + This.ErrorMsg := "Invalid statement handle!" + Return False + } + If (Index < 1) || (Index > This.ParamCount) { + This.ErrorMsg := "Invalid parameter index!" + Return False + } + If (Types[Type] = "") { + This.ErrorMsg := "Invalid parameter type!" + Return False + } + If (Type = "Blob") { ; ---------------------------------------------------------------------------------------- + ; Param3 = BLOB pointer, Param4 = BLOB size in bytes + If Param3 Is Not Integer + { + This.ErrorMsg := "Invalid blob pointer!" + Return False + } + If Param4 Is Not Integer + { + This.ErrorMsg := "Invalid blob size!" + Return False + } + ; Let SQLite always create a copy of the BLOB + RC := DllCall("SQlite3.dll\sqlite3_bind_blob", "Ptr", This._Handle, "Int", Index, "Ptr", Param3 + , "Int", Param4, "Ptr", -1, "Cdecl Int") + If (ErrorLeveL) { + This.ErrorMsg := "DllCall sqlite3_bind_blob failed!" + This.ErrorCode := ErrorLevel + Return False + } + If (RC) { + This.ErrorMsg := This._ErrMsg() + This.ErrorCode := RC + Return False + } + } + Else If (Type = "Double") { ; --------------------------------------------------------------------------------- + ; Param3 = double value + If Param3 Is Not Float + { + This.ErrorMsg := "Invalid value for double!" + Return False + } + RC := DllCall("SQlite3.dll\sqlite3_bind_double", "Ptr", This._Handle, "Int", Index, "Double", Param3 + , "Cdecl Int") + If (ErrorLeveL) { + This.ErrorMsg := "DllCall sqlite3_bind_double failed!" + This.ErrorCode := ErrorLevel + Return False + } + If (RC) { + This.ErrorMsg := This._ErrMsg() + This.ErrorCode := RC + Return False + } + } + Else If (Type = "Int") { ; ------------------------------------------------------------------------------------ + ; Param3 = integer value + If Param3 Is Not Integer + { + This.ErrorMsg := "Invalid value for int!" + Return False + } + RC := DllCall("SQlite3.dll\sqlite3_bind_int", "Ptr", This._Handle, "Int", Index, "Int", Param3 + , "Cdecl Int") + If (ErrorLeveL) { + This.ErrorMsg := "DllCall sqlite3_bind_int failed!" + This.ErrorCode := ErrorLevel + Return False + } + If (RC) { + This.ErrorMsg := This._ErrMsg() + This.ErrorCode := RC + Return False + } + } + Else If (Type = "Text") { ; ----------------------------------------------------------------------------------- + ; Param3 = zero-terminated string + This._DB._StrToUTF8(Param3, ByRef UTF8) + ; Let SQLite always create a copy of the text + RC := DllCall("SQlite3.dll\sqlite3_bind_text", "Ptr", This._Handle, "Int", Index, "Ptr", &UTF8 + , "Int", -1, "Ptr", -1, "Cdecl Int") + If (ErrorLeveL) { + This.ErrorMsg := "DllCall sqlite3_bind_text failed!" + This.ErrorCode := ErrorLevel + Return False + } + If (RC) { + This.ErrorMsg := This._ErrMsg() + This.ErrorCode := RC + Return False + } + } + Return True + } + + ; ---------------------------------------------------------------------------------------------------------------- + ; METHOD Step Evaluate the prepared statement. + ; Parameters: None + ; Return values: On success - True + ; On failure - False, ErrorMsg / ErrorCode contain additional information + ; Remarks: You must call ST.Reset() before you can call ST.Step() again. + ; ---------------------------------------------------------------------------------------------------------------- + Step() { + This.ErrorMsg := "" + This.ErrorCode := 0 + If !(This._Handle) { + This.ErrorMsg := "Invalid statement handle!" + Return False + } + RC := DllCall("SQlite3.dll\sqlite3_step", "Ptr", This._Handle, "Cdecl Int") + If (ErrorLevel) { + This.ErrorMsg := "DllCall sqlite3_step failed!" + This.ErrorCode := ErrorLevel + Return False + } + If (RC <> This._DB._ReturnCode("SQLITE_DONE")) + && (RC <> This._DB._ReturnCode("SQLITE_ROW")) { + This.ErrorMsg := This._DB.ErrMsg() + This.ErrorCode := RC + Return False + } + Return True + } + ; ---------------------------------------------------------------------------------------------------------------- + ; METHOD Reset Reset the prepared statement. + ; Parameters: ClearBindings - Clear bound SQL parameter values (True/False) + ; Return values: On success - True + ; On failure - False, ErrorMsg / ErrorCode contain additional information + ; Remarks: After a call of this method you can access the query result via Next() again. + ; ---------------------------------------------------------------------------------------------------------------- + Reset(ClearBindings := True) { + This.ErrorMsg := "" + This.ErrorCode := 0 + If !(This._Handle) { + This.ErrorMsg := "Invalid statement handle!" + Return False + } + RC := DllCall("SQlite3.dll\sqlite3_reset", "Ptr", This._Handle, "Cdecl Int") + If (ErrorLevel) { + This.ErrorMsg := "DllCall sqlite3_reset failed!" + This.ErrorCode := ErrorLevel + Return False + } + If (RC) { + This.ErrorMsg := This._DB._ErrMsg() + This.ErrorCode := RC + Return False + } + If (ClearBindings) { + RC := DllCall("SQlite3.dll\sqlite3_clear_bindings", "Ptr", This._Handle, "Cdecl Int") + If (ErrorLevel) { + This.ErrorMsg := "DllCall sqlite3_clear_bindings failed!" + This.ErrorCode := ErrorLevel + Return False + } + If (RC) { + This.ErrorMsg := This._DB._ErrMsg() + This.ErrorCode := RC + Return False + } + } + Return True + } + ; ---------------------------------------------------------------------------------------------------------------- + ; METHOD Free Free the prepared statement object. + ; Parameters: None + ; Return values: On success - True + ; On failure - False, ErrorMsg / ErrorCode contain additional information + ; Remarks: After the call of this method further access on the statement object is impossible. + ; ---------------------------------------------------------------------------------------------------------------- + Free() { + This.ErrorMsg := "" + This.ErrorCode := 0 + If !(This._Handle) + Return True + RC := DllCall("SQlite3.dll\sqlite3_finalize", "Ptr", This._Handle, "Cdecl Int") + If (ErrorLevel) { + This.ErrorMsg := "DllCall sqlite3_finalize failed!" This.ErrorCode := ErrorLevel Return False } @@ -259,8 +506,9 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { This.ErrorCode := RC Return False } - This._DB._Queries.Remove(This._Handle) + This._DB._Stmts.Delete(This._Handle) This._Handle := 0 + This._DB := 0 Return True } } @@ -271,6 +519,7 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { This._Path := "" ; Database path (String) This._Handle := 0 ; Database handle (Pointer) This._Queries := {} ; Valid queries (Object) + This._Stmts := {} ; Valid prepared statements (Object) If (This.Base._RefCount = 0) { SQLiteDLL := This.Base._SQLiteDLL If !FileExist(SQLiteDLL) @@ -283,7 +532,9 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { ExitApp } This.Base.Version := StrGet(DllCall("SQlite3.dll\sqlite3_libversion", "Cdecl UPtr"), "UTF-8") - If (SubStr(RegExReplace(This.Base.Version, "\."), 1, 2) < This.Base._MinVersion) { + SQLVersion := StrSplit(This.Base.Version, ".") + MinVersion := StrSplit(This.Base._MinVersion, ".") + If (SQLVersion[1] < MinVersion[1]) || ((SQLVersion[1] = MinVersion[1]) && (SQLVersion[2] < MinVersion[2])){ DllCall("FreeLibrary", "Ptr", DLL) MsgBox, 16, SQLite ERROR, % "Version " . This.Base.Version . " of SQLite3.dll is not supported!`n`n" . "You can download the current version from www.sqlite.org!" @@ -307,10 +558,10 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { ; =================================================================================================================== ; PRIVATE _StrToUTF8 ; =================================================================================================================== - _StrToUTF8(Str) { + _StrToUTF8(Str, ByRef UTF8) { VarSetCapacity(UTF8, StrPut(Str, "UTF-8"), 0) StrPut(Str, &UTF8, "UTF-8") - Return UTF8 + Return &UTF8 } ; =================================================================================================================== ; PRIVATE _UTF8ToStr @@ -395,7 +646,7 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { ; Remarks: If DBPath is empty in write mode, a database called ":memory:" is created in memory ; and deletet on call of CloseDB. ; =================================================================================================================== - OpenDB(DBPath, Access = "W", Create = True) { + OpenDB(DBPath, Access := "W", Create := True) { Static SQLITE_OPEN_READONLY := 0x01 ; Database opened as read-only Static SQLITE_OPEN_READWRITE := 0x02 ; Database opened as read-write Static SQLITE_OPEN_CREATE := 0x04 ; Database will be created if not exists @@ -422,7 +673,7 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { Flags |= SQLITE_OPEN_CREATE } This._Path := DBPath - UTF8 := This._StrToUTF8(DBPath) + This._StrToUTF8(DBPath, UTF8) RC := DllCall("SQlite3.dll\sqlite3_open_v2", "Ptr", &UTF8, "PtrP", HDB, "Int", Flags, "Ptr", 0, "Cdecl Int") If (ErrorLevel) { This._Path := "" @@ -507,19 +758,19 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { ; Return values: On success - True, the number of changed rows is given in property Changes ; On failure - False, ErrorMsg / ErrorCode contain additional information ; =================================================================================================================== - Exec(SQL, Callback = "") { + Exec(SQL, Callback := "") { This.ErrorMsg := "" This.ErrorCode := 0 This.SQL := SQL If !(This._Handle) { - This.ErrorMsg := "Invalid dadabase handle!" + This.ErrorMsg := "Invalid database handle!" Return False } CBPtr := 0 Err := 0 If (FO := Func(Callback)) && (FO.MinParams = 4) CBPtr := RegisterCallback(Callback, "F C", 4, &SQL) - UTF8 := This._StrToUTF8(SQL) + This._StrToUTF8(SQL, UTF8) RC := DllCall("SQlite3.dll\sqlite3_exec", "Ptr", This._Handle, "Ptr", &UTF8, "Int", CBPtr, "Ptr", Object(This) , "PtrP", Err, "Cdecl Int") CallError := ErrorLevel @@ -551,17 +802,17 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { ; Return values: On success - True, TB contains the result object ; On failure - False, ErrorMsg / ErrorCode contain additional information ; =================================================================================================================== - GetTable(SQL, ByRef TB, MaxResult = 0) { + GetTable(SQL, ByRef TB, MaxResult := 0) { TB := "" This.ErrorMsg := "" This.ErrorCode := 0 This.SQL := SQL If !(This._Handle) { - This.ErrorMsg := "Invalid dadabase handle!" + This.ErrorMsg := "Invalid database handle!" Return False } - If !RegExMatch(SQL, "i)^\s*SELECT\s") { - This.ErrorMsg := "Method " . A_ThisFunc . " requires a SELECT-Statement!" + If !RegExMatch(SQL, "i)^\s*(SELECT|PRAGMA)\s") { + This.ErrorMsg := A_ThisFunc . " requires a query statement!" Return False } Names := "" @@ -572,7 +823,7 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { MaxResult := 0 If (MaxResult < -2) MaxResult := 0 - UTF8 := This._StrToUTF8(SQL) + This._StrToUTF8(SQL, UTF8) RC := DllCall("SQlite3.dll\sqlite3_get_table", "Ptr", This._Handle, "Ptr", &UTF8, "PtrP", Table , "IntP", Rows, "IntP", Cols, "PtrP", Err, "Cdecl Int") If (ErrorLevel) { @@ -632,6 +883,55 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { Return True } ; =================================================================================================================== + ; Prepared statement 10:54 2019.07.05. by Dixtroy + ; DB := new SQLiteDB + ; DB.OpenDB(DBFileName) + ; DB.Prepare 1 or more, just once + ; DB.Step 1 or more on prepared one, repeatable + ; DB.Finalize at the end + ; =================================================================================================================== + ; =================================================================================================================== + ; METHOD Prepare Prepare database table for further actions. + ; Parameters: SQL - SQL statement to be compiled + ; ByRef ST - Variable to store the statement object (Class _Statement) + ; Return values: On success - True, ST contains the statement object + ; On failure - False, ErrorMsg / ErrorCode contain additional information + ; Remarks: You have to pass one ? for each column you want to assign a value later. + ; =================================================================================================================== + Prepare(SQL, ByRef ST) { + This.ErrorMsg := "" + This.ErrorCode := 0 + This.SQL := SQL + If !(This._Handle) { + This.ErrorMsg := "Invalid database handle!" + Return False + } + If !RegExMatch(SQL, "i)^\s*(INSERT|UPDATE|REPLACE)\s") { + This.ErrorMsg := A_ThisFunc . " requires an INSERT/UPDATE/REPLACE statement!" + Return False + } + Stmt := 0 + This._StrToUTF8(SQL, UTF8) + RC := DllCall("SQlite3.dll\sqlite3_prepare_v2", "Ptr", This._Handle, "Ptr", &UTF8, "Int", -1 + , "PtrP", Stmt, "Ptr", 0, "Cdecl Int") + If (ErrorLeveL) { + This.ErrorMsg := A_ThisFunc . ": DllCall sqlite3_prepare_v2 failed!" + This.ErrorCode := ErrorLevel + Return False + } + If (RC) { + This.ErrorMsg := A_ThisFunc . ": " . This._ErrMsg() + This.ErrorCode := RC + Return False + } + ST := New This._Statement + ST.ParamCount := DllCall("SQlite3.dll\sqlite3_bind_parameter_count", "Ptr", This._Handle, "Cdecl Int") + ST._Handle := Stmt + ST._DB := This + This._Stmts[Stmt] := Stmt + Return True + } + ; =================================================================================================================== ; METHOD Query Get "recordset" object for prepared SELECT query ; Parameters: SQL - SQL SELECT statement ; ByRef RS - Variable to store the result object (Class _RecordSet) @@ -649,12 +949,12 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { This.ErrorMsg := "Invalid dadabase handle!" Return False } - If !RegExMatch(SQL, "i)^\s*SELECT\s") { - This.ErrorMsg := "Method " . A_ThisFunc . " requires a SELECT statement!" + If !RegExMatch(SQL, "i)^\s*(SELECT|PRAGMA)\s|") { + This.ErrorMsg := A_ThisFunc . " requires a query statement!" Return False } Query := 0 - UTF8 := This._StrToUTF8(SQL) + This._StrToUTF8(SQL, UTF8) RC := DllCall("SQlite3.dll\sqlite3_prepare_v2", "Ptr", This._Handle, "Ptr", &UTF8, "Int", -1 , "PtrP", Query, "Ptr", 0, "Cdecl Int") If (ErrorLeveL) { @@ -710,7 +1010,42 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { RS.HasRows := HasRows RS._Handle := Query RS._DB := This - This._Queries.Insert(Query, Query) + This._Queries[Query] := Query + Return True + } + ; =================================================================================================================== + ; METHOD CreateScalarFunc Create a scalar application defined function + ; Parameters: Name - the name of the function + ; Args - the number of arguments that the SQL function takes + ; Func - a pointer to AHK functions that implement the SQL function + ; Enc - specifies what text encoding this SQL function prefers for its parameters + ; Param - an arbitrary pointer accessible within the funtion with sqlite3_user_data() + ; Return values: On success - True + ; On failure - False, ErrorMsg / ErrorCode contain additional information + ; Documentation: www.sqlite.org/c3ref/create_function.html + ; =================================================================================================================== + CreateScalarFunc(Name, Args, Func, Enc := 0x0801, Param := 0) { + ; SQLITE_DETERMINISTIC = 0x0800 - the function will always return the same result given the same inputs + ; within a single SQL statement + ; SQLITE_UTF8 = 0x0001 + This.ErrorMsg := "" + This.ErrorCode := 0 + If !(This._Handle) { + This.ErrorMsg := "Invalid database handle!" + Return False + } + RC := DllCall("SQLite3.dll\sqlite3_create_function", "Ptr", This._Handle, "AStr", Name, "Int", Args, "Int", Enc + , "Ptr", Param, "Ptr", Func, "Ptr", 0, "Ptr", 0, "Cdecl Int") + If (ErrorLeveL) { + This.ErrorMsg := "DllCall sqlite3_create_function failed!" + This.ErrorCode := ErrorLevel + Return False + } + If (RC) { + This.ErrorMsg := This._ErrMsg() + This.ErrorCode := RC + Return False + } Return True } ; =================================================================================================================== @@ -724,13 +1059,13 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { This.ErrorCode := 0 This.SQL := "" If !(This._Handle) { - This.ErrorMsg := "Invalid dadabase handle!" + This.ErrorMsg := "Invalid database handle!" Return False } RowID := 0 RC := DllCall("SQLite3.dll\sqlite3_last_insert_rowid", "Ptr", This._Handle, "Cdecl Int64") If (ErrorLevel) { - This.ErrorMsg := "DLLCall sqlite3_last_insert_rowid failed!" + This.ErrorMsg := "DllCall sqlite3_last_insert_rowid failed!" This.ErrorCode := ErrorLevel Return False } @@ -748,13 +1083,13 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { This.ErrorCode := 0 This.SQL := "" If !(This._Handle) { - This.ErrorMsg := "Invalid dadabase handle!" + This.ErrorMsg := "Invalid database handle!" Return False } Rows := 0 RC := DllCall("SQLite3.dll\sqlite3_total_changes", "Ptr", This._Handle, "Cdecl Int") If (ErrorLevel) { - This.ErrorMsg := "DLLCall sqlite3_total_changes failed!" + This.ErrorMsg := "DllCall sqlite3_total_changes failed!" This.ErrorCode := ErrorLevel Return False } @@ -768,19 +1103,19 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { ; Return values: On success - True ; On failure - False, ErrorMsg / ErrorCode contain additional information ; =================================================================================================================== - SetTimeout(Timeout = 1000) { + SetTimeout(Timeout := 1000) { This.ErrorMsg := "" This.ErrorCode := 0 This.SQL := "" If !(This._Handle) { - This.ErrorMsg := "Invalid dadabase handle!" + This.ErrorMsg := "Invalid database handle!" Return False } If Timeout Is Not Integer Timeout := 1000 - RC := DllCall("SQLite3.dll\sqlite3_busy_timeout", "Ptr", This._Handle, "Cdecl Int") + RC := DllCall("SQLite3.dll\sqlite3_busy_timeout", "Ptr", This._Handle, "Int", Timeout, "Cdecl Int") If (ErrorLevel) { - This.ErrorMsg := "DLLCall sqlite3_busy_timeout failed!" + This.ErrorMsg := "DllCall sqlite3_busy_timeout failed!" This.ErrorCode := ErrorLevel Return False } @@ -798,22 +1133,22 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { ; Return values: On success - True ; On failure - False, ErrorMsg / ErrorCode contain additional information ; =================================================================================================================== - EscapeStr(ByRef Str, Quote = True) { + EscapeStr(ByRef Str, Quote := True) { This.ErrorMsg := "" This.ErrorCode := 0 This.SQL := "" If !(This._Handle) { - This.ErrorMsg := "Invalid dadabase handle!" + This.ErrorMsg := "Invalid database handle!" Return False } If Str Is Number Return True - OP := Quote ? "%Q" : "%q" - OP := This._StrToUTF8(OP) - UTF8 := This._StrToUTF8(Str) + VarSetCapacity(OP, 16, 0) + StrPut(Quote ? "%Q" : "%q", &OP, "UTF-8") + This._StrToUTF8(Str, UTF8) Ptr := DllCall("SQLite3.dll\sqlite3_mprintf", "Ptr", &OP, "Ptr", &UTF8, "Cdecl UPtr") If (ErrorLevel) { - This.ErrorMsg := "DLLCall sqlite3_mprintf failed!" + This.ErrorMsg := "DllCall sqlite3_mprintf failed!" This.ErrorCode := ErrorLevel Return False } @@ -840,7 +1175,7 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { This.ErrorMsg := "" This.ErrorCode := 0 If !(This._Handle) { - This.ErrorMsg := "Invalid dadabase handle!" + This.ErrorMsg := "Invalid database handle!" Return False } If !RegExMatch(SQL, "i)^\s*(INSERT|UPDATE|REPLACE)\s") { @@ -848,11 +1183,11 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { Return False } Query := 0 - UTF8 := This._StrToUTF8(SQL) + This._StrToUTF8(SQL, UTF8) RC := DllCall("SQlite3.dll\sqlite3_prepare_v2", "Ptr", This._Handle, "Ptr", &UTF8, "Int", -1 , "PtrP", Query, "Ptr", 0, "Cdecl Int") If (ErrorLeveL) { - This.ErrorMsg := A_ThisFunc . ": DLLCall sqlite3_prepare_v2 failed!" + This.ErrorMsg := A_ThisFunc . ": DllCall sqlite3_prepare_v2 failed!" This.ErrorCode := ErrorLevel Return False } @@ -870,7 +1205,7 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { RC := DllCall("SQlite3.dll\sqlite3_bind_blob", "Ptr", Query, "Int", BlobNum, "Ptr", Blob.Addr , "Int", Blob.Size, "Ptr", SQLITE_STATIC, "Cdecl Int") If (ErrorLeveL) { - This.ErrorMsg := A_ThisFunc . ": DLLCall sqlite3_prepare_v2 failed!" + This.ErrorMsg := A_ThisFunc . ": DllCall sqlite3_bind_blob failed!" This.ErrorCode := ErrorLevel Return False } @@ -882,7 +1217,7 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { } RC := DllCall("SQlite3.dll\sqlite3_step", "Ptr", Query, "Cdecl Int") If (ErrorLevel) { - This.ErrorMsg := A_ThisFunc . ": DLLCall sqlite3_step failed!" + This.ErrorMsg := A_ThisFunc . ": DllCall sqlite3_step failed!" This.ErrorCode := ErrorLevel Return False } @@ -893,7 +1228,7 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { } RC := DllCall("SQlite3.dll\sqlite3_finalize", "Ptr", Query, "Cdecl Int") If (ErrorLevel) { - This.ErrorMsg := A_ThisFunc . ": DLLCall sqlite3_finalize failed!" + This.ErrorMsg := A_ThisFunc . ": DllCall sqlite3_finalize failed!" This.ErrorCode := ErrorLevel Return False } @@ -904,4 +1239,22 @@ Class SQLiteDB Extends SQLiteDB.BaseClass { } Return True } +} +; ====================================================================================================================== +; Exemplary custom callback function regexp() +; Parameters: Context - handle to a sqlite3_context object +; ArgC - number of elements passed in Values (must be 2 for this function) +; Values - pointer to an array of pointers which can be passed to sqlite3_value_text(): +; 1. Needle +; 2. Haystack +; Return values: Call sqlite3_result_int() passing 1 (True) for a match, otherwise pass 0 (False). +; ====================================================================================================================== +SQLiteDB_RegExp(Context, ArgC, Values) { + Result := 0 + If (ArgC = 2) { + AddrN := DllCall("SQLite3.dll\sqlite3_value_text", "Ptr", NumGet(Values + 0, "UPtr"), "Cdecl UPtr") + AddrH := DllCall("SQLite3.dll\sqlite3_value_text", "Ptr", NumGet(Values + A_PtrSize, "UPtr"), "Cdecl UPtr") + Result := RegExMatch(StrGet(AddrH, "UTF-8"), StrGet(AddrN, "UTF-8")) + } + DllCall("SQLite3.dll\sqlite3_result_int", "Ptr", Context, "Int", !!Result, "Cdecl") ; 0 = false, 1 = trus } \ No newline at end of file diff --git a/sqlite3.dll b/sqlite3.dll index fb1d34e..a089c19 100644 Binary files a/sqlite3.dll and b/sqlite3.dll differ