VARCHAR type which is similarly unbounded when no length
specified.
+- mysql
+ - New DBAPI support for pymysql, a pure Python port
+ of MySQL-python. [ticket:1991]
+
- drizzle
- New dialect for Drizzle, a MySQL variant. Uses MySQL-python
for the DBAPI. [ticket:2003]
**DB2/Informix IDS**
ibm-db_ thirdparty thirdparty thirdparty thirdparty thirdparty thirdparty
**Drizzle**
-drizzle_ ``drizzle+mysqldb``\* yes development no yes yes
+mysql-python_ ``drizzle+mysqldb``\* yes development no yes yes
**Firebird**
kinterbasdb_ ``firebird+kinterbasdb``\* yes development no yes yes
**Informix**
`MySQL Connector/Python`_ ``mysql+mysqlconnector`` yes yes no yes yes
mysql-python_ ``mysql+mysqldb``\* yes development no yes yes
OurSQL_ ``mysql+oursql`` yes yes no yes yes
+pymysql_ ``mysql+pymysql`` yes development no yes yes
**Oracle**
cx_oracle_ ``oracle+cx_oracle``\* yes development no yes yes
`Oracle JDBC Driver`_ ``oracle+zxjdbc`` no no yes yes yes
.. _mysql-python: http://sourceforge.net/projects/mysql-python
.. _MySQL Connector/Python: https://launchpad.net/myconnpy
.. _OurSQL: http://packages.python.org/oursql/
+.. _pymysql: http://code.google.com/p/pymysql/
.. _PostgreSQL JDBC Driver: http://jdbc.postgresql.org/
.. _sqlite3: http://docs.python.org/library/sqlite3.html
.. _pysqlite: http://pypi.python.org/pypi/pysqlite/
.. _informixdb: http://informixdb.sourceforge.net/
.. _sapdb: http://www.sapdb.org/sapdbapi.html
.. _python-sybase: http://python-sybase.sourceforge.net/
-.. _drizzle: http://drizzle.org/
Further detail on dialects is available at :ref:`dialect_toplevel`.
.. automodule:: sqlalchemy.dialects.mysql.oursql
+pymysql Notes
+-------------
+
+.. automodule:: sqlalchemy.dialects.mysql.pymysql
+
MySQL-Connector Notes
----------------------
@classmethod
def dbapi(cls):
+ # is overridden when pymysql is used
return __import__('MySQLdb')
def do_executemany(self, cursor, statement, parameters, context=None):
client_flag = opts.get('client_flag', 0)
if self.dbapi is not None:
try:
- from MySQLdb.constants import CLIENT as CLIENT_FLAGS
+ CLIENT_FLAGS = __import__(self.dbapi.__package__+'.constants',
+ globals(), locals(),
+ ['CLIENT'], 0).CLIENT
client_flag |= CLIENT_FLAGS.FOUND_ROWS
except:
pass
-# -*- fill-column: 78 -*-
# drizzle/base.py
-# Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Michael Bayer mike_mp@zzzcomputing.com
-# and Jason Kirtland.
-# Copyright (C) 2010 Monty Taylor <mordred@inaugust.com>
+# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file>
+# Copyright (C) 2010-2011 Monty Taylor <mordred@inaugust.com>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
# the MIT License: http://www.opensource.org/licenses/mit-license.php
from sqlalchemy.dialects.mysql import base, mysqldb, oursql, \
- pyodbc, zxjdbc, mysqlconnector
+ pyodbc, zxjdbc, mysqlconnector, pymysql
# default dialect
base.dialect = mysqldb.dialect
Known Issues
-------------
-MySQL-python at least as of version 1.2.2 has a serious memory leak related
+MySQL-python version 1.2.2 has a serious memory leak related
to unicode conversion, a feature which is disabled via ``use_unicode=0``.
-The recommended connection form with SQLAlchemy is::
-
+Using a more recent version of MySQL-python is recommended. The
+recommended connection form with SQLAlchemy is::
+
engine = create_engine('mysql://scott:tiger@localhost/test?charset=utf8&use_unicode=0', pool_recycle=3600)
--- /dev/null
+# mysql/pymysql.py
+# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file>
+#
+# This module is part of SQLAlchemy and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""Support for the MySQL database via the pymysql adapter.
+
+pymysql is available at:
+
+ http://code.google.com/p/pymysql/
+
+Connecting
+----------
+
+Connect string::
+
+ mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]
+
+MySQL-Python Compatibility
+--------------------------
+
+The pymysql DBAPI is a pure Python port of the MySQL-python (MySQLdb) driver,
+and targets 100% compatibility. Most behavioral notes for MySQL-python apply to
+the pymysql driver as well.
+
+"""
+
+from sqlalchemy.dialects.mysql.mysqldb import MySQLDialect_mysqldb
+
+class MySQLDialect_pymysql(MySQLDialect_mysqldb):
+ driver = 'pymysql'
+
+ @classmethod
+ def dbapi(cls):
+ return __import__('pymysql')
+
+dialect = MySQLDialect_pymysql
\ No newline at end of file
# so remove some platforms that have wildly divergent
# callcounts.
__requires__ = 'python25',
- __unsupported_on__ = 'postgresql+pg8000',
+ __unsupported_on__ = 'postgresql+pg8000', 'mysql+pymysql'
@classmethod
def define_tables(cls, metadata):
postgresql_jython=postgresql+zxjdbc://scott:tiger@127.0.0.1:5432/test
mysql_jython=mysql+zxjdbc://scott:tiger@127.0.0.1:5432/test
mysql=mysql://scott:tiger@127.0.0.1:3306/test
+pymysql=mysql+pymysql://scott:tiger@127.0.0.1:3306/test?use_unicode=0&charset=utf8
oracle=oracle://scott:tiger@127.0.0.1:1521
oracle8=oracle://scott:tiger@127.0.0.1:1521/?use_ansi=0
mssql=mssql://scott:tiger@SQUAWK\\SQLEXPRESS/test
"MATCH (matchtable.title) AGAINST (%s IN BOOLEAN MODE)" % format)
@testing.fails_on('mysql+mysqldb', 'uses format')
+ @testing.fails_on('mysql+pymysql', 'uses format')
@testing.fails_on('mysql+oursql', 'uses format')
@testing.fails_on('mysql+pyodbc', 'uses format')
@testing.fails_on('mysql+zxjdbc', 'uses format')
conn.execute('delete from users')
# some psycopg2 versions bomb this.
- @testing.fails_on_everything_except('mysql+mysqldb',
+ @testing.fails_on_everything_except('mysql+mysqldb', 'mysql+pymysql',
'mysql+mysqlconnector', 'postgresql')
@testing.fails_on('postgresql+zxjdbc', 'sprintf not supported')
def test_raw_sprintf(self):
@testing.skip_if(lambda : testing.against('mysql+mysqldb'),
'db-api flaky')
@testing.fails_on_everything_except('postgresql+psycopg2',
- 'postgresql+pypostgresql', 'mysql+mysqlconnector')
+ 'postgresql+pypostgresql', 'mysql+mysqlconnector',
+ 'mysql+pymysql')
def test_raw_python(self):
for conn in testing.db, testing.db.connect():
conn.execute('insert into users (user_id, user_name) '
meta.drop_all()
engine.dispose()
+ @testing.fails_on('+pymysql',
+ "Buffers the result set and doesn't check for "
+ "connection close")
@testing.fails_on('+mysqldb',
"Buffers the result set and doesn't check for "
"connection close")
from sqlalchemy.engine import url as engine_url
- if config.db.driver == 'mysqldb' and config.db.dialect.name != 'drizzle':
- dbapi_ver = config.db.dialect.dbapi.version_info
- if (dbapi_ver < (1, 2, 1) or
- dbapi_ver in ((1, 2, 1, 'gamma', 1), (1, 2, 1, 'gamma', 2),
- (1, 2, 1, 'gamma', 3), (1, 2, 1, 'gamma', 5))):
- raise RuntimeError('Character set support unavailable with this '
- 'driver version: %s' % repr(dbapi_ver))
- else:
- url = url or config.db_url
- url = engine_url.make_url(url)
- url.query['charset'] = 'utf8'
- url.query['use_unicode'] = '0'
- url = str(url)
+ if config.db.dialect.name == 'mysql' and \
+ config.db.driver in ['mysqldb', 'pymysql']:
+ # note 1.2.1.gamma.6 or greater of MySQLdb
+ # needed here
+ url = url or config.db_url
+ url = engine_url.make_url(url)
+ url.query['charset'] = 'utf8'
+ url.query['use_unicode'] = '0'
+ url = str(url)
return testing_engine(url, options)
def dbapi_lastrowid(fn):
return _chain_decorators_on(
fn,
- fails_on_everything_except('mysql+mysqldb', 'mysql+oursql', 'sqlite+pysqlite')
+ fails_on_everything_except('mysql+mysqldb', 'mysql+oursql',
+ 'sqlite+pysqlite', 'mysql+pymysql')
)
def sane_multi_rowcount(fn):
def test_round_trip(self):
unicodedata = u"Alors vous imaginez ma surprise, au lever du jour, "\
- u"quand une drôle de petit voix m’a réveillé. Elle "\
+ u"quand une drôle de petite voix m’a réveillé. Elle "\
u"disait: « S’il vous plaît… dessine-moi un mouton! »"
unicode_table.insert().execute(unicode_varchar=unicodedata,unicode_text=unicodedata)
# vs. cursor.execute()
unicodedata = u"Alors vous imaginez ma surprise, au lever du jour, quand "\
- u"une drôle de petit voix m’a réveillé. "\
+ u"une drôle de petite voix m’a réveillé. "\
u"Elle disait: « S’il vous plaît… dessine-moi un mouton! »"
unicode_table.insert().execute(
"""ensure compiler processing works for UNIONs"""
unicodedata = u"Alors vous imaginez ma surprise, au lever du jour, quand "\
- u"une drôle de petit voix m’a réveillé. "\
+ u"une drôle de petite voix m’a réveillé. "\
u"Elle disait: « S’il vous plaît… dessine-moi un mouton! »"
unicode_table.insert().execute(unicode_varchar=unicodedata,unicode_text=unicodedata)
"""
unicodedata = u"Alors vous imaginez ma surprise, au lever du jour, quand "\
- u"une drôle de petit voix m’a réveillé. "\
+ u"une drôle de petite voix m’a réveillé. "\
u"Elle disait: « S’il vous plaît… dessine-moi un mouton! »"
# using Unicode explicly - warning should be emitted
"""checks String(unicode_error='ignore') is passed to underlying codec."""
unicodedata = u"Alors vous imaginez ma surprise, au lever du jour, quand "\
- u"une drôle de petit voix m’a réveillé. "\
+ u"une drôle de petite voix m’a réveillé. "\
u"Elle disait: « S’il vous plaît… dessine-moi un mouton! »"
asciidata = unicodedata.encode('ascii', 'ignore')
eq_(
x,
u'Alors vous imaginez ma surprise, au lever du jour, quand une '
- u'drle de petit voix ma rveill. Elle disait: Sil vous plat '
+ u'drle de petite voix ma rveill. Elle disait: Sil vous plat '
u'dessine-moi un mouton! '
)
elif engine.dialect.returns_unicode_strings:
eq_(e1.adapt(ENUM).schema, 'bar')
@testing.fails_on('mysql+mysqldb', "MySQL seems to issue a 'data truncated' warning.")
+ @testing.fails_on('mysql+pymysql', "MySQL seems to issue a 'data truncated' warning.")
def test_constraint(self):
assert_raises(exc.DBAPIError,
enum_table.insert().execute,