Permalink
Browse files

Initial sane version :-)

  • Loading branch information...
1 parent 2ea5adc commit 5bc381987484fe70a06d6b9f74eb1fc20a51d01c Luke Marsden committed Feb 8, 2011
Showing with 122 additions and 10 deletions.
  1. +67 −2 README
  2. +48 −0 example.py
  3. +7 −8 txmysql/protocol.py
View
69 README
@@ -1,5 +1,70 @@
-Rudimentary Twisted MySQL Protocol implementation.
+Rudimentary Twisted MySQL Protocol implementation. Consider this v0.1 alpha.
-With thanks to _habnabit for the intial code and his qbuf library.
+With thanks to _habnabit for the intial code (which did the vast majority of
+the heavy lifting) and his qbuf library.
+PREREQUISITES:
+ * qbuf (a Python C-extension)
+
+TODO:
+
+ * Support escaping arguments with '?' or '%s' style syntax (my preference
+ being for the former)
+ * Make it subclass ReconnectingClientFactory and handle reconnection
+ correctly
+
+ * Unit tests
+ * DateTime support (see pymysql.converters for how to do this)
+ * Iterator protocol for iterating over large results sets, lazily
+ loaded as they come back from the server
+ * DBAPI support - see http://www.python.org/dev/peps/pep-0249/
+ * Actually use the database=foo argument passed into the constructor
+ (need to set CLIENT_* flag)
+ * Code comments, and a lot of tidying up
+ * Test against more than just the version of MySQL 5.1 which happened
+ to be installed on my ThinkPad :-)
+
+EXAMPLE USAGE:
+
+Create an empty database called "foo" and set username and password in example.py.
+
+twistd -noy example.py
+
+Expected output:
+
+[]
+[[1, 'Hello world', 0], [2, 'Hello world', 1], [3, 'Hello world', 2], [4, 'Hello world', 3], [5, 'Hello world', 4], [6, 'Hello world', 5], [7, 'Hello world', 6], [8, 'Hello world', 7], [9, 'Hello world', 8], [10, 'Hello world', 9]]
+
+QUESTIONS?
+
+Email luke [at] hybrid-logic [dot] co [dot] uk
+
+
+LICENSE:
+
+Copyright 2011 Hybrid Logic Ltd. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY HYBIRD LOGIC LTD ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those of the
+authors and should not be interpreted as representing official policies, either expressed
+or implied, of Hybrid Logic Ltd.
View
@@ -0,0 +1,48 @@
+from txmysql.protocol import MySQLClientFactory, MySQLProtocol, error
+from twisted.internet import reactor, defer
+import secrets
+from twisted.application.service import Application
+
+factory = MySQLClientFactory(username='root', password=secrets.MYSQL_ROOT_PASS)
+
+class TestProtocol(MySQLProtocol):
+ def __init__(self, *args, **kw):
+ MySQLProtocol.__init__(self, *args, **kw)
+
+ def connectionMade(self):
+ MySQLProtocol.connectionMade(self)
+ self.do_test()
+
+ def connectionLost(self, reason):
+ print reason
+ def connectionFailed(self, reason):
+ print reason
+
+ @defer.inlineCallbacks
+ def do_test(self):
+ yield self.ready_deferred
+ yield self.select_db('foo')
+ try:
+ yield self.query("drop table testing")
+ except error.MySQLError, e:
+ print "Table doesn't exist, ignoring %s" % str(e)
+
+ yield self.query("""create table testing (
+ id int primary key auto_increment,
+ strings varchar(255),
+ numbers int)""")
+
+ results = yield self.fetchall("select * from testing")
+ print results # should be []
+
+ for i in range(10):
+ yield self.query("insert into testing set strings='Hello world', numbers=%i" % i)
+ results = yield self.fetchall("select * from testing")
+ print results
+
+factory.protocol = TestProtocol
+#factory = policies.SpewingFactory(factory)
+reactor.connectTCP('127.0.0.1', 3306, factory)
+
+application = Application("Telnet Echo Server")
+
View
@@ -259,15 +259,13 @@ def ping(self):
import pprint; pprint.pprint(result)
@operation
- def do_query(self, query):
+ def query(self, query):
+ "A query with no response data"
with util.DataPacker(self) as p:
p.write('\x03')
p.write(query)
-
- while True:
- result = yield self.read_result()
- if not result['eof']['flags'] & 8:
- break
+
+ ret = yield self.read_result()
@operation
def prepare(self, query):
@@ -300,9 +298,10 @@ def fetch(self, stmt_id, rows, types):
defer.returnValue((rows, more_rows))
@defer.inlineCallbacks
- def runQuery(self, query):
+ def fetchall(self, query):
result = yield self.prepare(query)
types = yield self.execute(result['stmt_id'])
+
all_rows = []
while True:
rows, more_rows = yield self.fetch(result['stmt_id'], 2, types)
@@ -316,7 +315,7 @@ def runQuery(self, query):
class MySQLClientFactory(ClientFactory):
protocol = MySQLProtocol
- def __init__(self, username, password, database):
+ def __init__(self, username, password, database=None):
self.username = username
self.password = password
self.database = database

0 comments on commit 5bc3819

Please sign in to comment.