windows 下 c 写的 python 的 mysql 扩展
由于现在还没有太多相关的中文文档,看手册理解的比较慢。
主要是通过底层的<mysql.h>中的函数,简单封装成了一个模块(mysql)中的两个类(Connection、Result)以及若干方法。
环境:window 7、mingw gcc 4.4.0、python 3.2、mysql 5.5。
代码:
mysql.c
1 #include <Python.h>
2 #include <windows.h>
3 #include <mysql.h>
4
5 /* Exceptions */
6 static PyObject *ConnectionError;
7 static PyObject *QueryError;
8 static PyObject *FetchError;
9
10 /* Result */
11 typedef struct {
12 PyObject_HEAD
13 Py_ssize_t res;
14 } Result;
15
16 static void Result_dealloc(Result *self)
17 {
18 mysql_free_result((MYSQL_RES *) self->res);
19 Py_TYPE(self)->tp_free((PyObject *) self);
20 }
21
22 static PyObject *Result_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
23 {
24 Result *self;
25 self = (Result *) type->tp_alloc(type, 0);
26 if (self == NULL)
27 {
28 return NULL;
29 }
30
31 self->res = 0;
32 return (PyObject *) self;
33 }
34
35 static int Result_init(PyObject *self, PyObject *args, PyObject *kwds)
36 {
37 return 0;
38 }
39
40 static PyObject *Result_iter(PyObject *self)
41 {
42 Py_INCREF(self);
43 return self;
44 }
45
46 static PyObject *Result_iternext(PyObject *self)
47 {
48 MYSQL_RES *res = (MYSQL_RES *) ((Result *) self)->res;
49
50 MYSQL_ROW row;
51 row = mysql_fetch_row(res);
52 if (row == NULL)
53 {
54 return NULL;
55 }
56
57 Py_ssize_t size = (Py_ssize_t) mysql_num_fields(res);
58 PyObject *tup;
59 tup = PyTuple_New(size);
60 if (tup == NULL)
61 {
62 return NULL;
63 }
64
65 int i;
66 for (i = 0; i < size; i++)
67 {
68 PyObject *cell;
69 if (row[i] == NULL)
70 cell = PyBytes_FromString("");
71 else
72 cell = PyBytes_FromString(row[i]);
73
74 if (cell == NULL)
75 {
76 Py_XDECREF(tup);
77 return NULL;
78 }
79
80 PyTuple_SetItem(tup, i, cell);
81 }
82
83 return tup;
84 }
85
86 static PyTypeObject Result_Type = {
87 PyVarObject_HEAD_INIT(NULL, 0)
88 "mysql.Result", /* tp_name */
89 sizeof(Result), /* tp_basicsize */
90 0, /* tp_itemsize */
91 (destructor) Result_dealloc, /* tp_dealloc */
92 0, /* tp_print */
93 0, /* tp_getattr */
94 0, /* tp_setattr */
95 0, /* tp_reserved */
96 0, /* tp_repr */
97 0, /* tp_as_number */
98 0, /* tp_as_sequence */
99 0, /* tp_as_mapping */
100 0, /* tp_hash */
101 0, /* tp_call */
102 0, /* tp_str */
103 0, /* tp_getattro */
104 0, /* tp_setattro */
105 0, /* tp_as_buffer */
106 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
107 "mysql.Result", /* tp_doc */
108 0, /* tp_traverse */
109 0, /* tp_clear */
110 0, /* tp_richcompare */
111 0, /* tp_weaklistoffset */
112 Result_iter, /* tp_iter */
113 Result_iternext, /* tp_iternext */
114 0, /* tp_methods */
115 0, /* tp_members */
116 0, /* tp_getset */
117 0, /* tp_base */
118 0, /* tp_dict */
119 0, /* tp_descr_get */
120 0, /* tp_descr_set */
121 0, /* tp_dictoffset */
122 (initproc) Result_init, /* tp_init */
123 0, /* tp_alloc */
124 Result_new, /* tp_new */
125 };
126
127 /* Connection */
128 typedef struct {
129 PyObject_HEAD
130 Py_ssize_t conn;
131 } Connection;
132
133 static void Connection_dealloc(Connection *self)
134 {
135 MYSQL *conn = (MYSQL *) self->conn;
136 if (conn)
137 {
138 mysql_close(conn);
139 }
140
141 Py_TYPE(self)->tp_free((PyObject *) self);
142 }
143
144 static PyObject *Connection_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
145 {
146 Connection *self;
147 self = (Connection *) type->tp_alloc(type, 0);
148 if (self == NULL)
149 {
150 return NULL;
151 }
152
153 self->conn = 0;
154 return (PyObject *) self;
155 }
156
157 static int Connection_init(PyObject *self, PyObject *args, PyObject *kwds)
158 {
159 const char *host, *user, *password, *dbname;
160 if (!PyArg_ParseTuple(args, "s|s|s|s", &host, &user, &password, &dbname))
161 {
162 return -1;
163 }
164
165 MYSQL *conn;
166 conn = mysql_init(NULL);
167 if (mysql_real_connect(conn, host, user, password, dbname, 0, NULL, 0) == NULL)
168 {
169 PyErr_SetString(ConnectionError, "ConnectionError");
170 return -1;
171 }
172
173 MYSQL *tmp = (MYSQL *) ((Connection *) self)->conn;
174 if (tmp)
175 {
176 mysql_close(tmp);
177 }
178
179 ((Connection *) self)->conn = (Py_ssize_t) conn;
180 return 0;
181 }
182
183 static PyObject *Connection_query(PyObject *self, PyObject *args)
184 {
185 const char *sql;
186 if (!PyArg_ParseTuple(args, "s", &sql))
187 {
188 return NULL;
189 }
190
191 MYSQL *conn = (MYSQL *) ((Connection *) self)->conn;
192 if (mysql_query(conn, sql))
193 {
194 PyErr_SetString(QueryError, "QueryError");
195 return NULL;
196 }
197
198 Py_INCREF(Py_None);
199 return Py_None;
200 }
201
202 static PyObject *Connection_fetch(PyObject *self)
203 {
204 PyObject *r;
205 r = PyType_GenericAlloc(&Result_Type, 1);
206 if (r == NULL)
207 {
208 return NULL;
209 }
210
211 MYSQL *conn = (MYSQL *) ((Connection *) self)->conn;
212 MYSQL_RES *res;
213 res = mysql_store_result(conn);
214 if (res == NULL)
215 {
216 PyErr_SetString(FetchError, "FetchError");
217
218 Py_DECREF(r);
219 return NULL;
220 }
221
222 ((Result *) r)->res = (Py_ssize_t) res;
223 return r;
224 }
225
226 static PyMethodDef Connection_methods[] = {
227 {"query", (PyCFunction) Connection_query, METH_VARARGS, "Connection.query"},
228 {"fetch", (PyCFunction) Connection_fetch, METH_NOARGS, "Connection.fetch"},
229 {NULL}
230 };
231
232 static PyTypeObject Connection_Type = {
233 PyVarObject_HEAD_INIT(NULL, 0)
234 "mysql.Connection", /* tp_name */
235 sizeof(Connection), /* tp_basicsize */
236 0, /* tp_itemsize */
237 (destructor) Connection_dealloc, /* tp_dealloc */
238 0, /* tp_print */
239 0, /* tp_getattr */
240 0, /* tp_setattr */
241 0, /* tp_reserved */
242 0, /* tp_repr */
243 0, /* tp_as_number */
244 0, /* tp_as_sequence */
245 0, /* tp_as_mapping */
246 0, /* tp_hash */
247 0, /* tp_call */
248 0, /* tp_str */
249 0, /* tp_getattro */
250 0, /* tp_setattro */
251 0, /* tp_as_buffer */
252 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
253 "mysql.Connection", /* tp_doc */
254 0, /* tp_traverse */
255 0, /* tp_clear */
256 0, /* tp_richcompare */
257 0, /* tp_weaklistoffset */
258 0, /* tp_iter */
259 0, /* tp_iternext */
260 Connection_methods, /* tp_methods */
261 0, /* tp_members */
262 0, /* tp_getset */
263 0, /* tp_base */
264 0, /* tp_dict */
265 0, /* tp_descr_get */
266 0, /* tp_descr_set */
267 0, /* tp_dictoffset */
268 (initproc) Connection_init, /* tp_init */
269 0, /* tp_alloc */
270 Connection_new, /* tp_new */
271 };
272
273 /* module */
274 static PyModuleDef mysql_module = {
275 PyModuleDef_HEAD_INIT,
276 "mysql",
277 "mysql module",
278 -1,
279 NULL, NULL, NULL, NULL, NULL
280 };
281
282 PyMODINIT_FUNC PyInit_mysql(void)
283 {
284 PyObject* m;
285
286 if (PyType_Ready(&Connection_Type) < 0)
287 return NULL;
288 if (PyType_Ready(&Result_Type) < 0)
289 return NULL;
290
291 m = PyModule_Create(&mysql_module);
292 if (m == NULL)
293 return NULL;
294
295 ConnectionError = PyErr_NewException("mysql.ConnectionError", NULL, NULL);
296 QueryError = PyErr_NewException("mysql.QueryError", NULL, NULL);
297 FetchError = PyErr_NewException("mysql.FetchError", NULL, NULL);
298 Py_INCREF(ConnectionError);
299 Py_INCREF(QueryError);
300 Py_INCREF(FetchError);
301 PyModule_AddObject(m, "ConnectionError", ConnectionError);
302 PyModule_AddObject(m, "QueryError", QueryError);
303 PyModule_AddObject(m, "FetchError", FetchError);
304
305 Py_INCREF(&Connection_Type);
306 Py_INCREF(&Result_Type);
307 PyModule_AddObject(m, "Connection", (PyObject *) &Connection_Type);
308 PyModule_AddObject(m, "Result", (PyObject *) &Result_Type);
309
310 return m;
311 }
312
313 int main(int argc, char **argv)
314 {
315 PyImport_AppendInittab("mysql", PyInit_mysql);
316 Py_SetProgramName((wchar_t *) argv[0]);
317 Py_Initialize();
318
319 PyImport_ImportModule("mysql");
320 }
Makefile
1 # Makefile
2 all:
3 gcc mysql.c -c -I/python/include -I/mysql/include
4 gcc mysql.o -shared -o mysql.pyd -L/python/libs -lpython32 -L/mysql/lib -lmysql
5 strip mysql.pyd
6
7 clean:
8 del /s *.bak *.o *.pyd
test.py
1 # test.py
2 import mysql
3 try:
4 db = mysql.Connection('localhost', 'root', '', 'blog')
5 db.query(r"set names 'utf8'");
6 db.query(r'select * from weibo order by rand() limit 5')
7 for row in db.fetch():
8 print('#{:s} {:s}\n{:s}'.format(row[0].decode(), row[1].decode(), row[2].decode()));
9 except mysql.ConnectionError:
10 print('ConnectionError')
11 except mysql.QueryError:
12 print('QueryError')