如何使用 ctypes 编码和包装 sqlite3 回调所需的 python 函数?
How to code and wrap with ctypes a python function needed as callback by sqlite3?
我正在尝试从 python 执行 sqlite3_exec
以逐行提取数据库的内容。根据 C API,我需要一个回调函数来执行迭代。在互联网的大量帮助下,我编写了以下代码:
更新了@eryksun 的建议
import ctypes
def extractor(unused, num_columns, pcolumn, pcolumn_name):
for column in range(0,num_columns):
if pcolumn[i] != None:
print pcolumn[i]
sqlite3DLL = ctypes.CDLL("C:\Python\PYTHON\DLLs\sqlite3.dll")
SQLITE_OPEN_READONLY = 1
null_ptr = ctypes.c_void_p(None)
p_src_db = ctypes.c_void_p(None)
ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.POINTER(ctypes.c_char_p), ctypes.POINTER(ctypes.c_char_p))
callback_func = callback_type(extractor)
connect = sqlite3DLL.sqlite3_open_v2(DatabasePath, ctypes.byref(p_src_db), SQLITE_OPEN_READONLY, null_ptr)
connect = sqlite3DLL.sqlite3_exec(DatabasePath, "SELECT * FROM *", callback_func, None, null_ptr)
sqlite3DLL.sqlite3_close(DatabasePath)
在继续 python 回调函数之前,我有一些疑问:
-
"SELECT * FROM *"
是否可能 SQL 语句 以避免提供 table 的名称(因为我不知道)?
- 两个函数
sqlite3_open_v2
和sqlite3_exec
的第一个参数是数据库的路径吗?
如果一切正常,我们可以继续处理回调函数。根据我在网上查到的,C回调函数应该有点类似于:
callback(void *NotUsed, int argc, char **argv, char **azColName)
for (int i = 0; i < argc; i++) {printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL")}
这就是为什么我对您在我的代码中看到的 CFUNCTYPE
进行编码。我如何编写一个 python 函数来匹配可能用数据库内容填充列表所需的回调?
我已经在代码上添加了建议的更改,回调函数只是打印值来验证输出。但它不会工作我得到一个错误:
con = sqlite3DLL.sqlite3_exec(FastenerLibraryPath, "SELECT * FROM *", callback_func, None, null_ptr)
WindowsError: exception: access violation writing 0x0000000000000009
非常感谢!
最终工作版(@eryksun 评论和@MarkTolonen 解决方案)
import ctypes
def extractor(unused, num_columns, pcolumn, pcolumn_name):
print ','.join(["''" if x is None else "'"+x+"'" for x in pcolumn[:num_columns]])
return 0
sqlite3DLL = ctypes.CDLL("C:\Python\PYTHON\DLLs\sqlite3.dll")
SQLITE_OPEN_READONLY = 1
null_ptr = ctypes.c_void_p(None)
p_src_db = ctypes.c_void_p(None)
ctypes.CFUNCTYPE(ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.POINTER(ctypes.c_char_p), ctypes.POINTER(ctypes.c_char_p))
callback_func = callback_type(extractor)
connect = sqlite3DLL.sqlite3_open_v2(DatabasePath, ctypes.byref(p_src_db), SQLITE_OPEN_READONLY, None)
connect = sqlite3DLL.sqlite3_exec(p_src_db, b"SELECT * FROM Pin", callback_func, None, None)
sqlite3DLL.sqlite3_close(p_src_db)
这是有效的,但是,我会研究 argtypes
和不透明类型。
谢谢大家!
使用以下数据库在 Python 2.7 和 Python 3.6 中测试(如果更改 DLL 路径):
create table tbl1(one varchar(10), two smallint);
insert into tbl1 values('hello',10);
insert into tbl1 values('goodbye',20);
代码:
# I know, bad form, but it makes the code easier to read for an example
from ctypes import *
# This was missing the 2nd c_int parameter.
CALLBACK = CFUNCTYPE(c_int, c_void_p, c_int, POINTER(c_char_p), POINTER(c_char_p))
@CALLBACK
def extractor(unused, num_columns, pcolumn, pcolumn_name):
print(pcolumn[:num_columns])
return 0 # needs to return 0 from callback or will abort.
sqlite3DLL = CDLL(r"C:\Python27\DLLs\sqlite3.dll")
SQLITE_OPEN_READONLY = 1
p_src_db = c_void_p()
sqlite3DLL.sqlite3_open_v2(b'test.db', byref(p_src_db), SQLITE_OPEN_READONLY, None)
# pass the handle returned by above as first parameter below
sqlite3DLL.sqlite3_exec(p_src_db, b'SELECT * FROM tbl1', extractor, None, None)
sqlite3DLL.sqlite3_close(p_src_db)
输出:
['hello', '10']
['goodbye', '20']
我还建议设置 argtypes
,因为它有助于捕获类型错误,并且对于某些参数类型(如 c_double),它是必需的。
sqlite3DLL.sqlite3_open_v2.argtypes = c_char_p, POINTER(c_void_p), c_int,c_char_p
sqlite3DLL.sqlite3_open_v2.restype = c_int
sqlite3DLL.sqlite3_exec.argtypes = c_void_p,c_char_p,CALLBACK,c_void_p,POINTER(c_char_p)
sqlite3DLL.sqlite3_open_v2.restype = c_int
我正在尝试从 python 执行 sqlite3_exec
以逐行提取数据库的内容。根据 C API,我需要一个回调函数来执行迭代。在互联网的大量帮助下,我编写了以下代码:
更新了@eryksun 的建议
import ctypes
def extractor(unused, num_columns, pcolumn, pcolumn_name):
for column in range(0,num_columns):
if pcolumn[i] != None:
print pcolumn[i]
sqlite3DLL = ctypes.CDLL("C:\Python\PYTHON\DLLs\sqlite3.dll")
SQLITE_OPEN_READONLY = 1
null_ptr = ctypes.c_void_p(None)
p_src_db = ctypes.c_void_p(None)
ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.POINTER(ctypes.c_char_p), ctypes.POINTER(ctypes.c_char_p))
callback_func = callback_type(extractor)
connect = sqlite3DLL.sqlite3_open_v2(DatabasePath, ctypes.byref(p_src_db), SQLITE_OPEN_READONLY, null_ptr)
connect = sqlite3DLL.sqlite3_exec(DatabasePath, "SELECT * FROM *", callback_func, None, null_ptr)
sqlite3DLL.sqlite3_close(DatabasePath)
在继续 python 回调函数之前,我有一些疑问:
-
"SELECT * FROM *"
是否可能 SQL 语句 以避免提供 table 的名称(因为我不知道)? - 两个函数
sqlite3_open_v2
和sqlite3_exec
的第一个参数是数据库的路径吗?
如果一切正常,我们可以继续处理回调函数。根据我在网上查到的,C回调函数应该有点类似于:
callback(void *NotUsed, int argc, char **argv, char **azColName)
for (int i = 0; i < argc; i++) {printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL")}
这就是为什么我对您在我的代码中看到的 CFUNCTYPE
进行编码。我如何编写一个 python 函数来匹配可能用数据库内容填充列表所需的回调?
我已经在代码上添加了建议的更改,回调函数只是打印值来验证输出。但它不会工作我得到一个错误:
con = sqlite3DLL.sqlite3_exec(FastenerLibraryPath, "SELECT * FROM *", callback_func, None, null_ptr)
WindowsError: exception: access violation writing 0x0000000000000009
非常感谢!
最终工作版(@eryksun 评论和@MarkTolonen 解决方案)
import ctypes
def extractor(unused, num_columns, pcolumn, pcolumn_name):
print ','.join(["''" if x is None else "'"+x+"'" for x in pcolumn[:num_columns]])
return 0
sqlite3DLL = ctypes.CDLL("C:\Python\PYTHON\DLLs\sqlite3.dll")
SQLITE_OPEN_READONLY = 1
null_ptr = ctypes.c_void_p(None)
p_src_db = ctypes.c_void_p(None)
ctypes.CFUNCTYPE(ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.POINTER(ctypes.c_char_p), ctypes.POINTER(ctypes.c_char_p))
callback_func = callback_type(extractor)
connect = sqlite3DLL.sqlite3_open_v2(DatabasePath, ctypes.byref(p_src_db), SQLITE_OPEN_READONLY, None)
connect = sqlite3DLL.sqlite3_exec(p_src_db, b"SELECT * FROM Pin", callback_func, None, None)
sqlite3DLL.sqlite3_close(p_src_db)
这是有效的,但是,我会研究 argtypes
和不透明类型。
谢谢大家!
使用以下数据库在 Python 2.7 和 Python 3.6 中测试(如果更改 DLL 路径):
create table tbl1(one varchar(10), two smallint);
insert into tbl1 values('hello',10);
insert into tbl1 values('goodbye',20);
代码:
# I know, bad form, but it makes the code easier to read for an example
from ctypes import *
# This was missing the 2nd c_int parameter.
CALLBACK = CFUNCTYPE(c_int, c_void_p, c_int, POINTER(c_char_p), POINTER(c_char_p))
@CALLBACK
def extractor(unused, num_columns, pcolumn, pcolumn_name):
print(pcolumn[:num_columns])
return 0 # needs to return 0 from callback or will abort.
sqlite3DLL = CDLL(r"C:\Python27\DLLs\sqlite3.dll")
SQLITE_OPEN_READONLY = 1
p_src_db = c_void_p()
sqlite3DLL.sqlite3_open_v2(b'test.db', byref(p_src_db), SQLITE_OPEN_READONLY, None)
# pass the handle returned by above as first parameter below
sqlite3DLL.sqlite3_exec(p_src_db, b'SELECT * FROM tbl1', extractor, None, None)
sqlite3DLL.sqlite3_close(p_src_db)
输出:
['hello', '10']
['goodbye', '20']
我还建议设置 argtypes
,因为它有助于捕获类型错误,并且对于某些参数类型(如 c_double),它是必需的。
sqlite3DLL.sqlite3_open_v2.argtypes = c_char_p, POINTER(c_void_p), c_int,c_char_p
sqlite3DLL.sqlite3_open_v2.restype = c_int
sqlite3DLL.sqlite3_exec.argtypes = c_void_p,c_char_p,CALLBACK,c_void_p,POINTER(c_char_p)
sqlite3DLL.sqlite3_open_v2.restype = c_int