[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
}
相关推荐
superman超哥11 分钟前
04 深入 Oracle 并发世界:MVCC、锁、闩锁、事务隔离与并发性能优化的探索
数据库·oracle·性能优化·dba
engchina1 小时前
Neo4j 和 Python 初学者指南:如何使用可选关系匹配优化 Cypher 查询
数据库·python·neo4j
engchina1 小时前
使用 Cypher 查询语言在 Neo4j 中查找最短路径
数据库·neo4j
尘浮生1 小时前
Java项目实战II基于Spring Boot的光影视频平台(开发文档+数据库+源码)
java·开发语言·数据库·spring boot·后端·maven·intellij-idea
威哥爱编程1 小时前
SQL Server 数据太多如何优化
数据库·sql·sqlserver
小华同学ai1 小时前
AJ-Report:一款开源且非常强大的数据可视化大屏和报表工具
数据库·信息可视化·开源
Acrelhuang2 小时前
安科瑞5G基站直流叠光监控系统-安科瑞黄安南
大数据·数据库·数据仓库·物联网
YUJIANYUE2 小时前
PHP将指定文件夹下多csv文件[即多表]导入到sqlite单文件
jvm·sqlite·php
十叶知秋2 小时前
【jmeter】jmeter的线程组功能的详细介绍
数据库·jmeter·性能测试
瓜牛_gn4 小时前
mysql特性
数据库·mysql