python - 如何使用 ctypes 编写和包装 sqlite3 所需的 python 函数作为回调?

标签 python c sqlite callback ctypes

我正在尝试从 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 回调函数之前,我有一些疑问:

  1. “SELECT * FROM *”是否是一个可能的SQL语句以避免提供表名(因为我不知道)?
  2. sqlite3_open_v2sqlite3_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 - 如何使用 ctypes 编写和包装 sqlite3 所需的 python 函数作为回调?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44437521/

相关文章:

python - 如何将数据帧从 Cloud Datalab 导出到 BigQuery 表?

c - 为什么没有用于 signed long 的套接字 ntol?

java - 创建唯一复合列的语法 - Android SQLiteOpenHelper

python - 使用 Warnsdorff 规则的 Knight' tour 给出了奇怪大小的错误输出 moSTLy

python - 找到与另外 2 个点共线的 3d 点

python - YouTube api 获得 Python 观看次数最多的前 10 名

c - 中断对外部模块/DLL 的所有调用

c - 可以将用户内存中的字符串传递给 printk 吗?

c - 如果不操纵它如何改变值(value)?

c# - ASP.NET Core 测试 - 在夹具中初始化 InMemory SQLite dbcontext 时获取 NullReferenceException