Skip to content

Commit ba5ccd9

Browse files
committed
Merge branch 'master' of github.com:PyMySQL/mysqlclient-python
2 parents b493d2e + 074f152 commit ba5ccd9

File tree

5 files changed

+133
-8
lines changed

5 files changed

+133
-8
lines changed

MySQLdb/connections.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ class Connection(_mysql.connection):
6363
"""MySQL Database Connection Object"""
6464

6565
default_cursor = cursors.Cursor
66+
waiter = None
6667

6768
def __init__(self, *args, **kwargs):
6869
"""
69-
7070
Create a connection to the database. It is strongly recommended
7171
that you only use keyword parameters. Consult the MySQL C API
7272
documentation for more information.
@@ -150,9 +150,13 @@ class object, used to create cursors (keyword only)
150150
If True, autocommit is enabled.
151151
If None, autocommit isn't set and server default is used.
152152
153+
waiter
154+
Callable accepts fd as an argument. It is called after sending
155+
query and before reading response.
156+
This is useful when using with greenlet and async io.
157+
153158
There are a number of undocumented, non-standard methods. See the
154159
documentation for the MySQL C API for some hints on what they do.
155-
156160
"""
157161
from MySQLdb.constants import CLIENT, FIELD_TYPE
158162
from MySQLdb.converters import conversions
@@ -195,6 +199,7 @@ class object, used to create cursors (keyword only)
195199

196200
# PEP-249 requires autocommit to be initially off
197201
autocommit = kwargs2.pop('autocommit', False)
202+
self.waiter = kwargs2.pop('waiter', None)
198203

199204
super(Connection, self).__init__(*args, **kwargs2)
200205
self.cursorclass = cursorclass
@@ -266,6 +271,14 @@ def cursor(self, cursorclass=None):
266271
"""
267272
return (cursorclass or self.cursorclass)(self)
268273

274+
def query(self, query):
275+
if self.waiter is not None:
276+
self.send_query(query)
277+
self.waiter(self.fileno())
278+
self.read_query_result()
279+
else:
280+
_mysql.connection.query(self, query)
281+
269282
def __enter__(self):
270283
if self.get_autocommit():
271284
self.query("BEGIN")

README.md

-6
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,3 @@ This is a fork of [MySQLdb1](https://github.com/farcepest/MySQLdb1).
77

88
This project adds Python 3 support and bug fixes.
99
I hope this fork is merged back to MySQLdb1 like distribute was merged back to setuptools.
10-
11-
TODO
12-
----
13-
14-
* A 1.3.0 release that will support Python 2.7 and 3.3-3.4
15-
* Release binary wheel for Windows.

_mysql.c

+74
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,16 @@ static int _mysql_ConnectionObject_clear(
756756
return 0;
757757
}
758758

759+
static char _mysql_ConnectionObject_fileno__doc__[] =
760+
"Return underlaying fd for connection";
761+
762+
static PyObject *
763+
_mysql_ConnectionObject_fileno(
764+
_mysql_ConnectionObject *self)
765+
{
766+
return PyInt_FromLong(self->connection.net.fd);
767+
}
768+
759769
static char _mysql_ConnectionObject_close__doc__[] =
760770
"Close the connection. No further activity possible.";
761771

@@ -1963,8 +1973,10 @@ _mysql_ConnectionObject_query(
19631973
{
19641974
char *query;
19651975
int len, r;
1976+
MYSQL *mysql = &(self->connection);
19661977
if (!PyArg_ParseTuple(args, "s#:query", &query, &len)) return NULL;
19671978
check_connection(self);
1979+
19681980
Py_BEGIN_ALLOW_THREADS
19691981
r = mysql_real_query(&(self->connection), query, len);
19701982
Py_END_ALLOW_THREADS
@@ -1974,6 +1986,50 @@ _mysql_ConnectionObject_query(
19741986
}
19751987

19761988

1989+
static char _mysql_ConnectionObject_send_query__doc__[] =
1990+
"Send a query. Same to query() except not wait response.\n\n\
1991+
Use read_query_result() before calling store_result() or use_result()\n";
1992+
1993+
static PyObject *
1994+
_mysql_ConnectionObject_send_query(
1995+
_mysql_ConnectionObject *self,
1996+
PyObject *args)
1997+
{
1998+
char *query;
1999+
int len, r;
2000+
MYSQL *mysql = &(self->connection);
2001+
if (!PyArg_ParseTuple(args, "s#:query", &query, &len)) return NULL;
2002+
check_connection(self);
2003+
2004+
Py_BEGIN_ALLOW_THREADS
2005+
r = mysql_send_query(mysql, query, len);
2006+
Py_END_ALLOW_THREADS
2007+
if (r) return _mysql_Exception(self);
2008+
Py_INCREF(Py_None);
2009+
return Py_None;
2010+
}
2011+
2012+
2013+
static char _mysql_ConnectionObject_read_query_result__doc__[] =
2014+
"Read result of query sent by send_query().\n";
2015+
2016+
static PyObject *
2017+
_mysql_ConnectionObject_read_query_result(
2018+
_mysql_ConnectionObject *self)
2019+
{
2020+
char *query;
2021+
int len, r;
2022+
MYSQL *mysql = &(self->connection);
2023+
check_connection(self);
2024+
2025+
Py_BEGIN_ALLOW_THREADS
2026+
r = (int)mysql_read_query_result(mysql);
2027+
Py_END_ALLOW_THREADS
2028+
if (r) return _mysql_Exception(self);
2029+
Py_INCREF(Py_None);
2030+
return Py_None;
2031+
}
2032+
19772033
static char _mysql_ConnectionObject_select_db__doc__[] =
19782034
"Causes the database specified by db to become the default\n\
19792035
(current) database on the connection specified by mysql. In subsequent\n\
@@ -2344,6 +2400,12 @@ static PyMethodDef _mysql_ConnectionObject_methods[] = {
23442400
METH_VARARGS,
23452401
_mysql_ConnectionObject_close__doc__
23462402
},
2403+
{
2404+
"fileno",
2405+
(PyCFunction)_mysql_ConnectionObject_fileno,
2406+
METH_NOARGS,
2407+
_mysql_ConnectionObject_fileno__doc__
2408+
},
23472409
{
23482410
"dump_debug_info",
23492411
(PyCFunction)_mysql_ConnectionObject_dump_debug_info,
@@ -2428,6 +2490,18 @@ static PyMethodDef _mysql_ConnectionObject_methods[] = {
24282490
METH_VARARGS,
24292491
_mysql_ConnectionObject_query__doc__
24302492
},
2493+
{
2494+
"send_query",
2495+
(PyCFunction)_mysql_ConnectionObject_send_query,
2496+
METH_VARARGS,
2497+
_mysql_ConnectionObject_send_query__doc__,
2498+
},
2499+
{
2500+
"read_query_result",
2501+
(PyCFunction)_mysql_ConnectionObject_read_query_result,
2502+
METH_NOARGS,
2503+
_mysql_ConnectionObject_read_query_result__doc__,
2504+
},
24312505
{
24322506
"select_db",
24332507
(PyCFunction)_mysql_ConnectionObject_select_db,

samples/waiter_gevent.py

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from __future__ import print_function
2+
"""Demo using Gevent with mysqlclient."""
3+
4+
import gevent.hub
5+
import select
6+
import MySQLdb
7+
8+
9+
def gevent_waiter(fd, hub=gevent.hub.get_hub()):
10+
hub.wait(hub.loop.io(fd, 1))
11+
12+
13+
def f(n):
14+
conn = MySQLdb.connect(user='root', waiter=gevent_waiter)
15+
cur = conn.cursor()
16+
cur.execute("SELECT SLEEP(%s)", (n,))
17+
cur.execute("SELECT 1+%s", (n,))
18+
print(cur.fetchall()[0])
19+
20+
21+
gevent.spawn(f, 1)
22+
gevent.spawn(f, 2)
23+
gevent.spawn(f, 3)
24+
gevent.spawn(f, 4)
25+
gevent.sleep(5)

samples/waiter_meinheld.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import meinheld
2+
import MySQLdb
3+
4+
5+
def meinheld_waiter(fd):
6+
meinheld.server.trampoline(fd, read=True, timeout=10)
7+
8+
9+
def app(env, start):
10+
cont = b"Hello, World\n"
11+
conn = MySQLdb.connect(user="root", waiter=meinheld_waiter)
12+
cur = conn.cursor()
13+
cur.execute("SELECT SLEEP(2)")
14+
start(b"200 OK", [('Content-Type', 'text/plain'), ('Content-Length', str(len(cont)))])
15+
return [cont]
16+
17+
18+
meinheld.server.listen(("0.0.0.0", 8080))
19+
meinheld.server.run(app)

0 commit comments

Comments
 (0)