# encoding: utf-8
# ---------------------------------------------------------
# SQLite3 For RMXP (by guoxiaomi)
# ---------------------------------------------------------
# version 0.3 at 2019/12/26
# ---------------------------------------------------------
# 注意:
# sqlite3_bind_double 无法正常使用,建议用格式化字符串拼接 SQL 语句
# sqlite3_column_double 在脚本里使用 sqlite3_column_blob 替代了
# bytesize,对更高版本的 RPG Maker,请搜索 "bytesize" 进行对应修改
#
# ---------------------------------------------------------
# 参考资料:
# 1. [url]https://renenyffenegger.ch/notes/development/databases/SQLite/VBA/index[/url]
# 2. [url]https://www.sqlite.org/download.html[/url],下载 32 位的 DLL
# ---------------------------------------------------------
# 示例
# ---------------------------------------------------------
# 1. 打开数据库
# ---------------------------------------------------------
# db = SQLite3_RM.new("test.db")
# db = SQLite3_RM.new("test.db", "123456")
# ---------------------------------------------------------
# 2. 执行 SQL 语句
# ---------------------------------------------------------
# db.exec("create table user (id, name, gold)")
# ---------------------------------------------------------
# 3. query方法执行时可以用占位符代替部分内容,
# 如果是查询语句则返回查询结果的数组
# 4. query方法携带代码块时会将搜索的结果传给代码块,
# 此时不会返回结果数组,而是返回nil
# ---------------------------------------------------------
# db.query("insert into user values (?, ?, %.2f)" % [10], [0, "rmxp"])
# db.query("select * from user") { |row| p row }
# ---------------------------------------------------------
# 5. 关闭数据库
# ---------------------------------------------------------
# db.close
# ---------------------------------------------------------
require "win32api" if not defined? Win32API
class SQLite3_RM
# ---------------------------------------------------------
# 配置 DLL 的位置和是否加密
# ---------------------------------------------------------
SQLite3_DLL = "sqlite3.dll"
SQLite3_Encrypt = true
# ---------------------------------------------------------
SQLite3_Open = Win32API.new(SQLite3_DLL, "sqlite3_open", "PP", "I")
SQLite3_Exec = Win32API.new(SQLite3_DLL, "sqlite3_exec", "LPLLP", "I")
SQLite3_Close = Win32API.new(SQLite3_DLL, "sqlite3_close", "L", "I")
SQLite3_Prepare_V2 = Win32API.new(SQLite3_DLL, "sqlite3_prepare_v2", "LPLPP", "I")
SQLite3_Bind_Int = Win32API.new(SQLite3_DLL, "sqlite3_bind_int", "LLL", "L")
# SQLite3_Bind_Double = Win32API.new(SQLite3_DLL, "sqlite3_bind_double", "LLD", "L")
SQLite3_Bind_Text = Win32API.new(SQLite3_DLL, "sqlite3_bind_text", "LLPIL", "L")
SQLite3_Bind_Null = Win32API.new(SQLite3_DLL, "sqlite3_bind_null", "LL", "L")
SQLite3_Bind_Blob = Win32API.new(SQLite3_DLL, "sqlite3_bind_blob", "LIPIL", "L")
SQLite3_Step = Win32API.new(SQLite3_DLL, "sqlite3_step", "L", "I")
SQLite3_Column_Count = Win32API.new(SQLite3_DLL, "sqlite3_column_count", "L", "I")
SQLite3_Column_Type = Win32API.new(SQLite3_DLL, "sqlite3_column_type", "LI", "I")
SQLite3_Column_Int = Win32API.new(SQLite3_DLL, "sqlite3_column_int", "LI", "I")
# SQLite3_Column_Double = Win32API.new(SQLite3_DLL, "sqlite3_column_double", "LI", "D")
SQLite3_Column_Text = Win32API.new(SQLite3_DLL, "sqlite3_column_text", "LI", "P")
SQLite3_Column_Blob = Win32API.new(SQLite3_DLL, "sqlite3_column_blob", "LI", "P")
SQLite3_Finalize = Win32API.new(SQLite3_DLL, "sqlite3_finalize", "L", "I")
SQLite3_INTEGER = 1
SQLite3_FLOAT = 2
SQLite3_TEXT = 3
SQLite3_BLOB = 4
SQLite3_NULL = 5
SQLite3_OK = 0
SQLite3_DONE = 101
def initialize(path)
db = [0].pack("L")
ret = SQLite3_Open.call(path, db)
raise "SQLite3 Open Failed!" if ret != SQLite3_OK
@db = db.unpack("L")[0]
end
def close
ret = SQLite3_Close.call(@db)
raise "SQLite3 Close Failed!" if ret != SQLite3_OK
end
def exec(query)
ret = SQLite3_Exec.call(@db, query, 0, 0, 0)
raise "SQLite3 Exec Failed!" if ret != SQLite3_OK
end
def query(query, bind = [])
results = []
# stmt
stmt = [0].pack("L")
ret = SQLite3_Prepare_V2.call(@db, query, -1, stmt, 0)
raise "SQLite3 Prepare Failed!" if ret != SQLite3_OK
stmt = stmt.unpack("L")[0]
# bind
bind.each_with_index do |var, index|
case var
when NilClass
SQLite3_Bind_Null.call(stmt, index + 1)
when Integer
SQLite3_Bind_Int.call(stmt, index + 1, var)
when String
# 注意此处,如果不是 RMXP 需要改成 var.bytesize
SQLite3_Bind_Text.call(stmt, index + 1, var, var.size, 0)
when Float
raise "SQLite3 Bind Failed!"
end
end
# column counts
col_count = SQLite3_Column_Count.call(stmt)
# step
until SQLite3_Step.call(stmt) == SQLite3_DONE
row = (0...col_count).collect { |col|
case SQLite3_Column_Type.call(stmt, col)
when SQLite3_INTEGER
SQLite3_Column_Int.call(stmt, col)
when SQLite3_FLOAT
SQLite3_Column_Blob.call(stmt, col).to_s.to_f
when SQLite3_TEXT
SQLite3_Column_Text.call(stmt, col).to_s
when SQLite3_BLOB
SQLite3_Column_Blob.call(stmt, col).to_s
when SQLite3_NULL
nil
end
}
if block_given?
yield row
else
results << row
end
end
SQLite3_Finalize.call(stmt)
if block_given?
return nil
else
return results
end
end
if SQLite3_Encrypt
SQLite3_Key = Win32API.new(SQLite3_DLL, "sqlite3_key", "LPI", "I")
SQLite3_Rekey = Win32API.new(SQLite3_DLL, "sqlite3_rekey", "LPI", "I")
def key(key_string)
SQLite3_Key.call(@db, key_string, key_string.size)
end
def rekey(key_string)
SQLite3_Rekey.call(@db, key_string, key_string.size)
end
alias _key_initialize initialize
def initialize(path, key = "")
_key_initialize(path)
if self.rekey(key) != 0
self.key(key)
end
end
end
end