[AHK V2]SQLite测试用例

AutoHotkey v2语言写的SQLite调用示例

; 创建一个SQLite对象
db :=  CSQLite()

; 打开数据库
if !db.OpenDB("example.db") {
    MsgBox "Failed to open database: " db.ErrorMsg
    ExitApp
}

; 执行SQL查询
sql := "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER);"
if !db.Exec(sql) {
    MsgBox "Failed to execute SQL: " db.ErrorMsg
    db.CloseDB()
    ExitApp
}

; 插入数据
sql := "INSERT INTO users (name, age) VALUES ('John Doe', 30);"
if !db.Exec(sql) {
    MsgBox "Failed to insert data: " db.ErrorMsg
    db.CloseDB()
    ExitApp
}

; 查询数据
sql := "SELECT * FROM users;"
TB := {}
if !db.GetTable(sql, &TB) {
    MsgBox "Failed to query data: " db.ErrorMsg
    db.CloseDB()
    ExitApp
}

; 显示查询结果
for row in TB.Rows {
    MsgBox "ID: " row[1] ", Name: " row[2] ", Age: " row[3]
}

; 关闭数据库
db.CloseDB()

SQLite类源代码:

/************************************************************************
 * @description SQLite class
 * @file CSQLite.ahk
 * @author thqby
 * @date 2022/04/23
 * @version 0.0.4
 * https://github.com/samfisherirl/Useful-AHK-v2-Libraries-and-Classes/blob/main/__thqby_Translation_SQLite_English/SQLite.ahk
 ***********************************************************************/
class CSQLite
{
	static Version := "", _SQLiteDLL := A_ScriptDir . "\SQLite3.dll", _RefCount := 0, hModule:=0
	static _MinVersion := "3.29", execfunc_ptr := 0, callback_gettable_ptr := 0

	__New(DllFolder:="") {
		this._Path := ""						; Database path											(String)
		this._Handle := 0					  ; Database handle										 (Pointer)
		if (CSQLite._RefCount = 0) {
			SQLiteDLL := CSQLite._SQLiteDLL
			if FileExist(DllFolder "\SQLite3.dll")
				SQLiteDLL := CSQLite._SQLiteDLL := DllFolder "\SQLite3.dll"
			if (!DllCall("GetModuleHandle", "Str", SQLiteDLL, "UPtr")
				&&!(CSQLite.hModule:=DllCall("LoadLibrary", "Str", SQLiteDLL, "UPtr"))){
				MsgBox("DLL " SQLiteDLL " does not exist!", "CSQLite Error", 16)
				ExitApp()
			}
			CSQLite.Version := StrGet(DllCall("SQLite3.dll\sqlite3_libversion", "Cdecl UPtr"), "UTF-8")
			SQLVersion := StrSplit(CSQLite.Version, ".")
			MinVersion := StrSplit(CSQLite._MinVersion, ".")
			if (SQLVersion[1] < MinVersion[1]) || ((SQLVersion[1] = MinVersion[1]) && (SQLVersion[2] < MinVersion[2])){
				DllCall("FreeLibrary", "Ptr", CSQLite.hModule), CSQLite.hModule:=0
				MsgBox("Version " . CSQLite.Version .  " of SQLite3.dll is not supported!`n`n"
					. "You can download the current version from www.sqlite.org!", "SQLite ERROR", 16)
				ExitApp()
			}
			CSQLite.execfunc_ptr := DllCall("GetProcAddress", "Ptr", DllCall("LoadLibrary", "Str", SQLiteDLL, "UPtr"), "AStr", "sqlite3_exec", "Ptr")
			CSQLite.callback_gettable_ptr := CallbackCreate(callback_gettable, "F C")
		}
		CSQLite._RefCount += 1
	}
	; ===================================================================================================================
	; DESTRUCTOR __Delete
	; ===================================================================================================================
	__Delete() {
		if (this._Thread.HasOwnProp("_Threadobj"))
			this._Thread._Threadobj:=""
		if (this._Handle)
			this.CloseDB()
		CSQLite._RefCount -= 1
		if (CSQLite._RefCount = 0) {
			CallbackFree(CSQLite.callback_gettable_ptr)
			if (CSQLite.hModule)
				DllCall("FreeLibrary", "Ptr", CSQLite.hModule)
		}
	}
	; ===================================================================================================================
	; PRIVATE _StrToUTF8
	; ===================================================================================================================
	_StrToUTF8(Str, &UTF8) => (VarSetStrCapacity(&UTF8, size:=StrPut(Str, "UTF-8")), StrPut(Str, StrPtr(UTF8), "UTF-8"), size)

	; ===================================================================================================================
	; PRIVATE _ErrMsg
	; ===================================================================================================================
	_UTF8ToStr(UTF8) => StrGet(StrPtr(UTF8), "UTF-8")
	
   ; ===================================================================================================================
   ; PRIVATE _ErrMsg
   ; ===================================================================================================================
	_ErrMsg() => ((RC := DllCall("SQLite3.dll\sqlite3_errmsg", "Ptr", this._Handle, "Cdecl UPtr")) ? StrGet(StrPtr(RC), "UTF-8") : "")
	
   ; ===================================================================================================================
   ; PRIVATE _ErrCode
   ; ===================================================================================================================
	_ErrCode() => DllCall("SQLite3.dll\sqlite3_errcode", "Ptr", this._Handle, "Cdecl Int")
	
   ; ===================================================================================================================
   ; PRIVATE _Changes
   ; ===================================================================================================================
	_Changes() => DllCall("SQLite3.dll\sqlite3_changes", "Ptr", this._Handle, "Cdecl Int")
	
   ; ===================================================================================================================
   ; PRIVATE _Returncode
   ; ===================================================================================================================
_ReturnMsg(RC) {
    static Msg := Map(1,    "SQL error or missing database"
	                , 2,    "SQLite internal logic error"
	                , 3,    "Access denied"
	                , 4,    "Callback function requested operation cancellation"
	                , 5,    "Database file is locked"
	                , 6,    "A table in the database is locked"
	                , 7,    "A malloc() function call failed"
	                , 8,    "Attempt to write to a read-only database"
	                , 9,    "Operation interrupted by sqlite3_interrupt() function"
	                , 10,   "Some disk I/O error occurred"
	                , 11,   "Incorrect database disk image"
	                , 12,   "Unknown opcode in sqlite3_file_control()"
	                , 13,   "Insertion failed due to database full"
	                , 14,   "Unable to open the database file"
	                , 15,   "Database lock protocol error"
	                , 16,   "Database is empty"
	                , 17,   "Data structure has changed"
	                , 18,   "String or binary data exceeds size limit"
	                , 19,   "Cancelled due to constraint violation"
	                , 20,   "Data type mismatch"
	                , 21,   "Incorrect library usage"
	                , 22,   "Use of unsupported operating system feature"
	                , 23,   "Authorization failed"
	                , 24,   "Attached database format error"
	                , 25,   "Second parameter passed to sqlite3_bind() is out of range"
	                , 26,   "The opened file is not a database file"
	                , 100,  "sqlite3_step() has produced a row result"
	                , 101,  "sqlite3_step() completed the execution operation")
    return Msg.HasKey(RC) ? Msg[RC] : ""
}
   ; ===================================================================================================================
   ; Properties
   ; ===================================================================================================================
	ErrorMsg := ""              ; Error message                           (String) 
	ErrorCode := 0              ; SQLite error code / ErrorLevel          (Variant)
	Changes := 0                ; Changes made by last call of Exec()     (Integer)
	SQL := ""                   ; Last executed SQL statement             (String)
	_Thread := {_Threadobj:0, queue:[], pexec:0, DB:0}
	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
		static MEMDB := ":memory:"
		this.ErrorMsg := "", this.ErrorCode := 0
		if (DBPath = "")
			DBPath := MEMDB
		if (DBPath = this._Path) && (this._Handle)
			return true
		if (this._Handle)
			return (this.ErrorMsg := "You must first close DB " . this._Path . "!", false)
		Flags := 0, Access := SubStr(Access, 1, 1)
		if (Access != "W") && (Access != "R")
			Access := "R"
		Flags := SQLITE_OPEN_READONLY
		if (Access = "W") {
			Flags := SQLITE_OPEN_READWRITE
			if (Create)
				Flags |= SQLITE_OPEN_CREATE
		}
		this._Path := DBPath, this._StrToUTF8(DBPath, &UTF8:="")
		if (RC := DllCall("SQLite3.dll\sqlite3_open_v2", "Ptr", StrPtr(UTF8), "Ptr*", &HDB:=0, "Int", Flags, "Ptr", 0, "Cdecl Int"))
			return (this._Path := "", this.ErrorMsg := this._ErrMsg(), this.ErrorCode := RC, false)
		this._Handle := HDB
		this.createScalarFunction(regexp, 2)
		this.createScalarFunction(regex_replace, 3)
		return true
	}
   ; ===================================================================================================================
   ; METHOD CloseDB        Close database
   ; Parameters:           None
   ; return values:        On success  - true
   ;                       On failure  - false, ErrorMsg / ErrorCode contain additional information
   ; ===================================================================================================================
	CloseDB() {
		this.ErrorMsg := "", this.ErrorCode := 0, this.SQL := ""
		if !(this._Handle)
			return true
		if (RC := DllCall("SQLite3.dll\sqlite3_close", "Ptr", this._Handle, "Cdecl Int"))
			return (this.ErrorMsg := this._ErrMsg(), this.ErrorCode := RC, false)
		this._Path := "", this._Handle := ""
		return true
	}
   ; ===================================================================================================================
	LoadOrSaveDb(DBPath, isSave:=0){
		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
		this.ErrorMsg := "", this.ErrorCode := 0, HDB := 0
		if (DBPath = "")
			return false
		if (!this._Handle)
			return (this.ErrorMsg := "You must first Open Memory DB!", false)
		Flags := SQLITE_OPEN_READWRITE, Flags |= SQLITE_OPEN_CREATE
		this._StrToUTF8(isSave?(tmp:=StrReplace(DBPath, ".db", ".tmp")):DBPath, &UTF8:="")
		if (RC := DllCall("SQLite3.dll\sqlite3_open_v2", "Ptr", StrPtr(UTF8), "Ptr*", &HDB, "Int", Flags, "Ptr", 0, "Cdecl Int"))
			return (this.ErrorMsg := this._ErrMsg(), this.ErrorCode := RC, false)
		pFrom := (isSave ? this._Handle : HDB), pTo   := (isSave ? HDB : this._Handle), this._StrToUTF8("main", &UTF8)
		pBackup := DllCall("SQLite3.dll\sqlite3_backup_init", "Ptr", pTo, "Ptr", StrPtr(UTF8), "Ptr", pFrom, "Ptr", StrPtr(UTF8), "Cdecl Int")
		if (pBackup){
			DllCall("SQLite3.dll\sqlite3_backup_step", "Ptr", pBackup, "Int", -1, "Cdecl Int")
			DllCall("SQLite3.dll\sqlite3_backup_finish", "Ptr", pBackup, "Cdecl Int")
		}
		RC := DllCall("SQLite3.dll\sqlite3_errcode", "Ptr", pTo, "Cdecl Int")
		DllCall("SQLite3.dll\sqlite3_close", "Ptr", HDB, "Cdecl Int")
		if (RC) {
			this._Path := "", this.ErrorMsg := this._ErrMsg(), this.ErrorCode := RC
			if (isSave)
				FileDelete tmp
			return false
		}
		if (isSave){
			if (FileGetSize(tmp, "K")<16)
				return (FileDelete(tmp), false)
			FileMove tmp, DBPath, 1
			this.Changes:=0
		}
		return true
	}
   ; ===================================================================================================================
   ; METHOD AttachDB       Add another database file to the current database connection
   ;                       http://www.sqlite.org/lang_attach.html
   ; Parameters:           DBPath      - Path of the database file
   ;                       DBAlias     - Database alias name used internally by SQLite
   ; return values:        On success  - true
   ;                       On failure  - false, ErrorMsg / ErrorCode contain additional information
   ; ===================================================================================================================
	AttachDB(DBPath, DBAlias) => this.Exec("ATTACH DATABASE '" . DBPath . "' As " . DBAlias . ";")

   ; ===================================================================================================================
   ; METHOD DetachDB       Detaches an additional database connection previously attached using AttachDB()
   ;                       http://www.sqlite.org/lang_detach.html
   ; Parameters:           DBAlias     - Database alias name used with AttachDB()
   ; return values:        On success  - true
   ;                       On failure  - false, ErrorMsg / ErrorCode contain additional information
   ; ===================================================================================================================
	DetachDB(DBAlias) => this.Exec("DETACH DATABASE " . DBAlias . ";")

	class Thread
	{
		Ptr:=0, pfunc:=0
		__New(funcobj, params, initflag:=0){
			this.pfunc:=pfunc:=CallbackCreate(funcobj)
			if (!this.Ptr:=DllCall("msvcrt\_beginthreadex", "Ptr", 0, "UInt", 0, "Ptr", pfunc, "Ptr", ObjPtrAddRef(params), "UInt", initflag, "UInt*", &threadid:=0))
				throw Error("create thread fail")
			this.threadid:=threadid
		}

		__Delete()=>(CallbackFree(this.pfunc), (this.ExitCode=259)?this.Terminate():"", (this.Ptr?DllCall("Kernel32\CloseHandle", "Ptr", this):0))
		Terminate(dwExitCode:=1)=>(DllCall("Kernel32\TerminateThread", "Ptr", this, "UInt", dwExitCode))
		ExitCode=>(DllCall("Kernel32\GetExitCodeThread", "Ptr", this, "UInt*", &Code:=0)?Code:0)
	}
	ExecByThread(SQL){
		if !(this._Handle)
			return (this.ErrorMsg := "Invalid database handle!", false)
		s:=StrPut(SQL, "UTF-8"), this._Thread.queue.Push(Buffer(s)), StrPut(SQL, this._Thread.queue[-1], "UTF-8")
		if (this._Thread.pexec)
			return
		this._Thread.DB:=this, this._Thread.pexec:=CSQLite.execfunc_ptr
		this._Thread._Threadobj:=CSQLite.Thread(_Exec, this._Thread)
		
		_Exec(pinfo){
			_Thread:=ObjFromPtr(pinfo), hdb:=_Thread.DB._Handle, pexec:=_Thread.pexec
			while (_Thread.queue.Has(1)){
				if (RC:=DllCall(pexec, "Ptr", hdb, "Ptr", _Thread.queue[1], "Ptr", 0, "Ptr", 0, "Ptr*", &Err:=0, "Cdecl Int")){
					ErrMsg:=_Thread.DB._ReturnMsg(RC), DllCall("SQLite3.dll\sqlite3_free", "Ptr", Err, "Cdecl")
					throw Error(ErrMsg)
				}
				_Thread.queue.RemoveAt(1)
			}
			_Thread.DB:="", _Thread.pexec:=0
			return 0
		}
	}
	Exec(SQL, pCallback := 0) {
		this.ErrorMsg := "", this.ErrorCode := 0, this.SQL := SQL
		if !(this._Handle)
			return (this.ErrorMsg := "Invalid database handle!", false)
		this._StrToUTF8(SQL, &UTF8:="")
		RC := DllCall(CSQLite.execfunc_ptr, "Ptr", this._Handle, "Ptr", StrPtr(UTF8), "Ptr", pCallback, "Ptr", ObjPtrAddRef(this), "Ptr*", &Err:=0, "Cdecl Int")
		if (RC) {
			this.ErrorMsg := this._ReturnMsg(RC)
			if (this.ErrorMsg = "")
				this.ErrorMsg := StrGet(Err, "UTF-8")
			this.ErrorCode := RC
			DllCall("SQLite3.dll\sqlite3_free", "Ptr", Err, "Cdecl")
			return false
		}
		this.Changes := this._Changes()
		return true
	}
	GetTable(SQL, &TB, pcall := 0) {
		this.ErrorMsg := "", this.ErrorCode := 0, this.SQL := SQL
		if !(this._Handle)
			return (this.ErrorMsg := "Invalid database handle!", false)
		this._StrToUTF8(SQL, &UTF8:=""), TB := {RowCount:0, Rows:[]}
		RC := DllCall(CSQLite.execfunc_ptr, "Ptr", this._Handle, "Ptr", StrPtr(UTF8)
			, "Ptr", (pcall>0?pcall:CSQLite.callback_gettable_ptr), "Ptr", ObjPtrAddRef(TB), "Ptr*", &Err:=0, "Cdecl Int")
		if (RC) {
			this.ErrorMsg := this._ReturnMsg(RC)
			if (this.ErrorMsg = "")
				this.ErrorMsg := StrGet(Err, "UTF-8"), this.Err := ""
			else
				this.Err := StrGet(Err, "UTF-8")
			this.ErrorCode := RC, DllCall("SQLite3.dll\sqlite3_free", "Ptr", Err, "Cdecl")
			return false
		} else if (pcall<0){
			RC := DllCall("SQlite3.dll\sqlite3_prepare_v2", "Ptr", This._Handle, "Ptr", StrPtr(UTF8), "Int", -1, "Ptr*", &Stmt:=0, "Ptr", 0, "Cdecl Int")
			TB.Cols := [], TB.ColCount := DllCall("SQlite3.dll\sqlite3_column_count", "Ptr", Stmt, "Cdecl Int")
			Loop TB.ColCount
				TB.Cols.Push(StrGet(DllCall("SQlite3.dll\sqlite3_column_name16", "Ptr", Stmt, "Int", A_Index - 1, "Cdecl UPtr"), "UTF-16"))
			DllCall("SQLite3.dll\sqlite3_finalize", "Ptr", Stmt, "Cdecl Int")
		}
		return true
	}
	createScalarFunction(func, params) {
		argType := Type(func)
		if (argType != "Func" && argType != "Closure")
			throw Error(this.__class "::" A_thisFunc " - First parameter not a Func object", -1)
		else if (!func.name)
			throw Error(this.__class "::" A_thisFunc " - Function must be named", -1)
		this.ErrorMsg := "", this.ErrorCode := 0, cb := CallbackCreate(func, "F C")
		if (err := DllCall("SQLite3.dll\sqlite3_create_function16", "Ptr", this._handle, "Str", func.name, "Int", params, "Int", 0x801, "Ptr", 0, "Ptr", cb, "Ptr", 0, "Ptr", 0, "Cdecl Int"))
			return (this.ErrorMsg := this._ErrMsg(), this.ErrorCode := err, false)
		return true
	}
	LastInsertRowID(&RowID) {
		this.ErrorMsg := "", this.ErrorCode := 0, this.SQL := ""
		if !(this._Handle)
			return (this.ErrorMsg := "Invalid database handle!", false)
		RowID := DllCall("SQLite3.dll\sqlite3_last_insert_rowid", "Ptr", this._Handle, "Cdecl Int64")
		return true
	}
	TotalChanges(&Rows) {
		this.ErrorMsg := "", this.ErrorCode := 0, this.SQL := ""
		if !(this._Handle)
			return (this.ErrorMsg := "Invalid database handle!", false)
		Rows := DllCall("SQLite3.dll\sqlite3_total_changes", "Ptr", this._Handle, "Cdecl Int")
		return true
	}
	SetTimeout(Timeout := 1000) {
		this.ErrorMsg := "", this.ErrorCode := 0, this.SQL := ""
		if !(this._Handle)
			return (this.ErrorMsg := "Invalid database handle!", false)
		if !IsInteger(Timeout)
			Timeout := 1000
		RC := DllCall("SQLite3.dll\sqlite3_busy_timeout", "Ptr", this._Handle, "Int", Timeout, "Cdecl Int")
		if (RC)
			return (this.ErrorMsg := this._ErrMsg(), this.ErrorCode := RC, false)
		return true
	}
}

regexp(Context, ArgC, vals) {
	regexNeedle := DllCall("SQLite3.dll\sqlite3_value_text16", "Ptr", NumGet(vals + 0, "Ptr"), "Cdecl Str")
	search := DllCall("SQLite3.dll\sqlite3_value_text16", "Ptr", NumGet(vals + A_PtrSize, "Ptr"), "Cdecl Str")
	DllCall("SQLite3.dll\sqlite3_result_int", "Ptr", Context, "Int", RegexMatch(search, regexNeedle), "Cdecl") ; 0 = false, 1 = true
}
regex_replace(Context, ArgC, vals) {
	search := DllCall("SQLite3.dll\sqlite3_value_text16", "Ptr", NumGet(vals + 0, "Ptr"), "Cdecl Str")
	regexNeedle := DllCall("SQLite3.dll\sqlite3_value_text16", "Ptr", NumGet(vals + A_PtrSize, "Ptr"), "Cdecl Str")
	Replacement := DllCall("SQLite3.dll\sqlite3_value_text16", "Ptr", NumGet(vals + A_PtrSize*2, "Ptr"), "Cdecl Str")
	DllCall("SQLite3.dll\sqlite3_result_text16", "Ptr", Context, "Str", RegExReplace(search, regexNeedle, Replacement), "Int", -1, "Ptr", 0, "Cdecl")
}
callback_gettable(TB, coln, vals, cols) {
	arr := Array(), TBobj := ObjFromPtrAddRef(TB)
	Loop coln
		arr.Push(StrGet(NumGet(vals + A_PtrSize * (A_Index - 1), "Ptr"), "UTF-8"))
	TBobj.Rows.Push(arr), TBobj.RowCount++
	return 0
}
相关推荐
南城花随雪。6 分钟前
硬盘(HDD)与固态硬盘(SSD)详细解读
数据库
儿时可乖了7 分钟前
使用 Java 操作 SQLite 数据库
java·数据库·sqlite
懒是一种态度9 分钟前
Golang 调用 mongodb 的函数
数据库·mongodb·golang
天海华兮11 分钟前
mysql 去重 补全 取出重复 变量 函数 和存储过程
数据库·mysql
gma9991 小时前
Etcd 框架
数据库·etcd
爱吃青椒不爱吃西红柿‍️1 小时前
华为ASP与CSP是什么?
服务器·前端·数据库
Yz98762 小时前
hive的存储格式
大数据·数据库·数据仓库·hive·hadoop·数据库开发
苏-言2 小时前
Spring IOC实战指南:从零到一的构建过程
java·数据库·spring
Ljw...2 小时前
索引(MySQL)
数据库·mysql·索引
菠萝咕噜肉i2 小时前
超详细:Redis分布式锁
数据库·redis·分布式·缓存·分布式锁