Skip to content
Snippets Groups Projects
Commit 42be27ee authored by Brian May's avatar Brian May
Browse files

Import python-mysqldb_1.3.7.orig.tar.gz

parent edd00306
Branches
No related merge requests found
Showing with 851 additions and 671 deletions
=====================
What's new in 1.3.6
=====================
Fix escape_string() doesn't work.
Remove `Cursor.__del__` to fix uncollectable circular reference on Python 3.3.
Add context manager support to `Cursor`. It automatically closes cursor on `__exit__`.
.. code-block::
with conn.cursor() as cur:
cur.execute("SELECT 1+1")
print(cur.fetchone())
# cur is now closed
=====================
What's new in 1.3.5
=====================
Fix TINYBLOB, MEDIUMBLOB and LONGBLOB are treated as string and decoded
to unicode or cause UnicodeError.
Fix aware datetime is formatted with timezone offset (e.g. "+0900").
=====================
What's new in 1.3.4
=====================
* Remove compiler warnings.
* Fix compile error when using libmariadbclient.
* Fix GIL deadlock while dealloc.
=====================
What's new in 1.3.3
=====================
* Fix exception reraising doesn't work.
=====================
What's new in 1.3.2
=====================
* Add send_query() and read_query_result() method to low level connection.
* Add waiter option.
=====================
What's new in 1.3.1
=====================
This is a first fork of MySQL-python.
Now named "mysqlclient"
* Support Python 3
* Add autocommit option
* Support microsecond in datetime field.
=====================
What's new in 1.2.4
=====================
final
=====
No changes.
rc 1
====
Fixed a dangling reference to the old types module.
beta 5
======
Another internal fix for handling remapped character sets.
_mysql.c was broken for the case where read_timeout was *not* available. (Issue #6)
Documentation was converted to sphinx but there is a lot of cleanup left to do.
beta 4
======
Added support for the MySQL read_timeout option. Contributed by
Jean Schurger (jean@schurger.org).
Added a workaround so that the MySQL character set utf8mb4 works with Python; utf8 is substituted
on the Python side.
beta 3
======
Unified test database configuration, and set up CI testing with Travis.
Applied several patches from André Malo (ndparker@users.sf.net) which fix some issues
with exception handling and reference counting and TEXT/BLOB conversion.
beta 2
======
Reverted an accidental change in the exception format. (issue #1)
Reverted some raise statements so that they will continue to work with Python < 2.6
beta 1
======
A lot of work has been done towards Python 3 compatibility, and avoiding warnings with Python 2.7.
This includes import changes, converting dict.has_kay(k) to k in dict, updating some test suite methods, etc.
Due to the difficulties of supporting Python 3 and Python < 2.7, 1.2.4 will support Python 2.4 though 2.7.
1.3.0 will support Python 3 and Python 2.7 and 2.6.
MySQLdb-2.0 is instead going to become moist-1.0. See https://github.com/farcepest/moist
The Windows build has been simplified, and I plan to correct pre-built i386 packages built
against the python.org Python-2.7 package and MySQL Connector/C-6.0. Contact me if you
need ia64 packages.
The connection's cursorclass (if not default) was being lost on reconnect.
Newer versions of MySQL don't use OpenSSL and therefore don't have HAVE_SSL defined, but they do have
a different SSL library. Fixed this so SSL support would be enabled in this case.
The regex that looked for SQL INSERT statement and VALUES in cursor.executemany() was made case-insensitive
again.
=====================
What's new in 1.2.3
=====================
ez_setup.py has been update to include various fixes that affect the build.
Better Python version and dependency detection as well as eliminate exception
warnings under Python 2.6.
Eliminated memory leaks related to Unicode and failed connections.
Corrected connection .escape() functionality.
Miscellaneous cleanups and and expanded testing suite to ensure ongoing release
quality.
=====================
What's new in 1.2.2
=====================
The build system has been completely redone and should now build
on Windows without any patching; uses setuptools.
Added compatibility for Python 2.5, including support for with statement.
connection.ping() now takes an optional boolean argument which can
enable (or disable) automatic reconnection.
Support returning SET columns as Python sets was removed due to an
API bug in MySQL; corresponding test removed.
Added a test for single-character CHAR columns.
BLOB columns are now returned as Python strings instead of byte arrays.
BINARY character columns are always returned as Python strings, and not
unicode.
Fixed a bug introduced in 1.2.1 where the new SHOW WARNINGS support broke
SSCursor.
Only encode the query (convert to a string) when it is a unicode instance;
re-encoding encoded strings would break things.
Make a deep copy of conv when connecting, since it can be modified.
Added support for new VARCHAR and BIT column types.
DBAPISet objects were broken, but nobody noticed.
========================
What's new in 1.2.1_p2
========================
There are some minor build fixes which probably only affect MySQL
older than 4.0.
If you had MySQL older than 4.1, the new charset and sql_mode
parameters didn't work right. In fact, it was impossible to create
a connection due to the charset problem.
If you are using MySQL-4.1 or newer, there is no practical difference
between 1.2.1 and 1.2.1_p2, and you don't need to upgrade.
=====================
What's new in 1.2.1
=====================
Switched to Subversion. Was going to do this for 1.3, but a
SourceForge CVS outage has forced the issue.
Mapped a lot of new 4.1 and 5.0 error codes to Python exceptions
Added an API call for mysql_set_character_set(charset) (MySQL > 5.0.7)
Added an API call for mysql_get_character_set_info() (MySQL > 5.0.10)
Revamped the build system. Edit site.cfg if necessary (probably not
in most cases)
Python-2.3 is now the minimum version.
Dropped support for mx.Datetime and stringtimes; always uses Python
datetime module now.
Improved unit tests
New connect() options:
* charset: sets character set, implies use_unicode
* sql_mode: sets SQL mode (i.e. ANSI, etc.; see MySQL docs)
When using MySQL-4.1 or newer, enables MULTI_STATEMENTS
When using MySQL-5.0 or newer, enables MULTI_RESULTS
When using MySQL-4.1 or newer, more detailed warning messages
are produced
SET columns returned as Python Set types; you can pass a Set as
a parameter to cursor.execute().
Support for the new MySQL-5.0 DECIMAL implementation
Support for Python Decimal type
Some use of weak references internally. Cursors no longer leak
if you don't close them. Connections still do, unfortunately.
ursor.fetchXXXDict() methods raise DeprecationWarning
cursor.begin() is making a brief reappearence.
cursor.callproc() now works, with some limitations.
=====================
What's new in 1.3.7
=====================
Support link args other than '-L' and '-l' from mysql_config.
Missing value for column without default value cause IntegrityError. (#33)
Support BIT type. (#38)
More tests for date and time columns. (#41)
Fix calling .execute() method for closed cursor cause TypeError. (#37)
Improve peformance to parse date. (#43)
Support geometry types (#49)
Fix warning while multi statement cause ProgrammingError. (#48)
=====================
What's new in 1.3.6
=====================
Fix escape_string() doesn't work.
Remove `Cursor.__del__` to fix uncollectable circular reference on Python 3.3.
Add context manager support to `Cursor`. It automatically closes cursor on `__exit__`.
.. code-block::
with conn.cursor() as cur:
cur.execute("SELECT 1+1")
print(cur.fetchone())
# cur is now closed
=====================
What's new in 1.3.5
=====================
Fix TINYBLOB, MEDIUMBLOB and LONGBLOB are treated as string and decoded
to unicode or cause UnicodeError.
Fix aware datetime is formatted with timezone offset (e.g. "+0900").
=====================
What's new in 1.3.4
=====================
* Remove compiler warnings.
* Fix compile error when using libmariadbclient.
* Fix GIL deadlock while dealloc.
=====================
What's new in 1.3.3
=====================
* Fix exception reraising doesn't work.
=====================
What's new in 1.3.2
=====================
* Add send_query() and read_query_result() method to low level connection.
* Add waiter option.
=====================
What's new in 1.3.1
=====================
This is a first fork of MySQL-python.
Now named "mysqlclient"
* Support Python 3
* Add autocommit option
* Support microsecond in datetime field.
=====================
What's new in 1.2.4
=====================
final
=====
No changes.
rc 1
====
Fixed a dangling reference to the old types module.
beta 5
======
Another internal fix for handling remapped character sets.
_mysql.c was broken for the case where read_timeout was *not* available. (Issue #6)
Documentation was converted to sphinx but there is a lot of cleanup left to do.
beta 4
======
Added support for the MySQL read_timeout option. Contributed by
Jean Schurger (jean@schurger.org).
Added a workaround so that the MySQL character set utf8mb4 works with Python; utf8 is substituted
on the Python side.
beta 3
======
Unified test database configuration, and set up CI testing with Travis.
Applied several patches from André Malo (ndparker@users.sf.net) which fix some issues
with exception handling and reference counting and TEXT/BLOB conversion.
beta 2
======
Reverted an accidental change in the exception format. (issue #1)
Reverted some raise statements so that they will continue to work with Python < 2.6
beta 1
======
A lot of work has been done towards Python 3 compatibility, and avoiding warnings with Python 2.7.
This includes import changes, converting dict.has_kay(k) to k in dict, updating some test suite methods, etc.
Due to the difficulties of supporting Python 3 and Python < 2.7, 1.2.4 will support Python 2.4 though 2.7.
1.3.0 will support Python 3 and Python 2.7 and 2.6.
MySQLdb-2.0 is instead going to become moist-1.0. See https://github.com/farcepest/moist
The Windows build has been simplified, and I plan to correct pre-built i386 packages built
against the python.org Python-2.7 package and MySQL Connector/C-6.0. Contact me if you
need ia64 packages.
The connection's cursorclass (if not default) was being lost on reconnect.
Newer versions of MySQL don't use OpenSSL and therefore don't have HAVE_SSL defined, but they do have
a different SSL library. Fixed this so SSL support would be enabled in this case.
The regex that looked for SQL INSERT statement and VALUES in cursor.executemany() was made case-insensitive
again.
=====================
What's new in 1.2.3
=====================
ez_setup.py has been update to include various fixes that affect the build.
Better Python version and dependency detection as well as eliminate exception
warnings under Python 2.6.
Eliminated memory leaks related to Unicode and failed connections.
Corrected connection .escape() functionality.
Miscellaneous cleanups and and expanded testing suite to ensure ongoing release
quality.
=====================
What's new in 1.2.2
=====================
The build system has been completely redone and should now build
on Windows without any patching; uses setuptools.
Added compatibility for Python 2.5, including support for with statement.
connection.ping() now takes an optional boolean argument which can
enable (or disable) automatic reconnection.
Support returning SET columns as Python sets was removed due to an
API bug in MySQL; corresponding test removed.
Added a test for single-character CHAR columns.
BLOB columns are now returned as Python strings instead of byte arrays.
BINARY character columns are always returned as Python strings, and not
unicode.
Fixed a bug introduced in 1.2.1 where the new SHOW WARNINGS support broke
SSCursor.
Only encode the query (convert to a string) when it is a unicode instance;
re-encoding encoded strings would break things.
Make a deep copy of conv when connecting, since it can be modified.
Added support for new VARCHAR and BIT column types.
DBAPISet objects were broken, but nobody noticed.
========================
What's new in 1.2.1_p2
========================
There are some minor build fixes which probably only affect MySQL
older than 4.0.
If you had MySQL older than 4.1, the new charset and sql_mode
parameters didn't work right. In fact, it was impossible to create
a connection due to the charset problem.
If you are using MySQL-4.1 or newer, there is no practical difference
between 1.2.1 and 1.2.1_p2, and you don't need to upgrade.
=====================
What's new in 1.2.1
=====================
Switched to Subversion. Was going to do this for 1.3, but a
SourceForge CVS outage has forced the issue.
Mapped a lot of new 4.1 and 5.0 error codes to Python exceptions
Added an API call for mysql_set_character_set(charset) (MySQL > 5.0.7)
Added an API call for mysql_get_character_set_info() (MySQL > 5.0.10)
Revamped the build system. Edit site.cfg if necessary (probably not
in most cases)
Python-2.3 is now the minimum version.
Dropped support for mx.Datetime and stringtimes; always uses Python
datetime module now.
Improved unit tests
New connect() options:
* charset: sets character set, implies use_unicode
* sql_mode: sets SQL mode (i.e. ANSI, etc.; see MySQL docs)
When using MySQL-4.1 or newer, enables MULTI_STATEMENTS
When using MySQL-5.0 or newer, enables MULTI_RESULTS
When using MySQL-4.1 or newer, more detailed warning messages
are produced
SET columns returned as Python Set types; you can pass a Set as
a parameter to cursor.execute().
Support for the new MySQL-5.0 DECIMAL implementation
Support for Python Decimal type
Some use of weak references internally. Cursors no longer leak
if you don't close them. Connections still do, unfortunately.
ursor.fetchXXXDict() methods raise DeprecationWarning
cursor.begin() is making a brief reappearence.
cursor.callproc() now works, with some limitations.
......@@ -8,57 +8,25 @@ MySQLdb Installation
Prerequisites
-------------
+ Python 2.3.4 or higher
+ Python 2.6, 2.7, 3.3 or higher
* http://www.python.org/
* Versions lower than 2.3 WON'T WORK.
* 2.4 is the primary test environment.
* Red Hat Linux:
- Make sure you have the Python development headers and libraries
(python-devel).
+ setuptools
* http://pypi.python.org/pypi/setuptools
+ MySQL 3.23.32 or higher
+ MySQL 5.0 or higher
* http://www.mysql.com/downloads/
* Versions lower than 3.22 definitely WON'T WORK.
* Versions lower than 3.22.19 might not work.
* MySQL-3.22 might work but isn't supported anymore. It's very old.
* MySQL-3.23 ought to work, but it's pretty elderly.
* MySQL-4.0 is supported, but not tested and slightly discouraged.
* MySQL-4.1 is supported. The prepared statements API is not
supported, and won't be until MySQLdb-1.3 or 2.0, if ever.
* MySQL-4.0 and MySQL-4.1 may work, but not supported.
* MySQL-5.0 is supported and tested, including stored procedures.
* MySQL-5.1 is supported (currently a release candidate) but untested.
It should work.
* MySQL-6.0 is sorta-kinda-supported (currently alpha) but untested.
It should work.
* Drizzle <https://launchpad.net/drizzle> is a fork of MySQL. So far
the C API looks really similar except everything is renamed.
Drizzle support probably won't happen in 1.2. There may be have to
be an entirely different module, but still using DB-API.
* MaxDB, formerly known as SAP DB (and maybe Adabas D?), is a
completely different animal. Use the sapdb.sql module that comes
with MaxDB.
* Red Hat Linux packages:
- mysql-devel to compile
......@@ -75,27 +43,6 @@ Prerequisites
- MySQL-shared to run if you compiled with MySQL-shared installed
* Transactions (particularly InnoDB tables) are supported for
MySQL-3.23 and up. You may need a special package from your vendor
with this support turned on.
+ zlib
* Required for MySQL-3.23 and newer.
* Red Hat Linux
- zlib-devel to compile
- zlib to run
+ openssl
* May be needed for MySQL-4.0 or newer, depending on compilation
options. If you need it, you probably already have it.
- you may need openssl-devel on some platforms
+ C compiler
* Most free software-based systems already have this, usually gcc.
......@@ -103,10 +50,8 @@ Prerequisites
* Most commercial UNIX platforms also come with a C compiler, or
you can also use gcc.
* If you have some Windows flavor, you usually have to pay extra
for this, or you can use Cygwin_.
.. _Cygwin: http://www.cygwin.com/
* If you have some Windows flavor, you should use Windows SDK or
Visual C++.
Building and installing
......@@ -134,12 +79,18 @@ edit the [options] section of site.cfg:
if True, try to link against a static library; otherwise link
against dynamic libraries (default). You may need static linking
to use the embedded server.
This option doesn't work for MySQL>5.6 since libmysqlclient
requires libstdc++. If you want to use, add `-lstdc++` to
mysql_config manually.
If `<mysql prefix>/lib` is not added to `/etc/ld.so.conf`, `import _mysql`
doesn't work. To fix this, (1) set `LD_LIBRARY_PATH`, or (2) add
`-Wl,-rpath,<mysql prefix>/lib` to ldflags in your mysql_config.
Finally, putting it together::
$ tar xfz MySQL-python-1.2.1.tar.gz
$ cd MySQL-python-1.2.1
$ tar xz mysqlclient-1.3.6.tar.gz
$ cd mysqlclient-1.3.6
$ # edit site.cfg if necessary
$ python setup.py build
$ sudo python setup.py install # or su first
......@@ -168,15 +119,6 @@ On Windows, you will definitely have to edit site.cfg since there is
no mysql_config in the MySQL package.
Zope
....
If you are using a binary package of Zope, you need run setup.py with
the python executable that came with Zope. Otherwise, you'll install
into the wrong Python tree and Zope (ZMySQLDA) will not be able to
find _mysql.
Binary Packages
---------------
......
......@@ -325,10 +325,9 @@ class Connection(_mysql.connection):
def warning_count(self):
"""Return the number of warnings generated from the
last query. This is derived from the info() method."""
from string import atoi
info = self.info()
if info:
return atoi(info.split()[-1])
return int(info.split()[-1])
else:
return 0
......
......@@ -21,7 +21,7 @@ restr = r"""
(?:
(?:
(?:\(
# ( - editor hightlighting helper
# ( - editor highlighting helper
.*
\))
|
......@@ -109,6 +109,14 @@ class BaseCursor(object):
def _warning_check(self):
from warnings import warn
if self._warnings:
# When there is next result, fetching warnings cause "command
# out of sync" error.
if self._result and self._result.has_next:
msg = "There are %d MySQL warnings." % (self._warnings,)
self.messages.append(msg)
warn(msg, self.Warning, 3)
return
warnings = self._get_db().show_warnings()
if warnings:
# This is done in two loops in case
......@@ -163,7 +171,7 @@ class BaseCursor(object):
if con is not None:
con = con()
if con is None:
self.errorhandler(self, ProgrammingError, "cursor closed")
raise ProgrammingError("cursor closed")
return con
def execute(self, query, args=None):
......@@ -204,36 +212,34 @@ class BaseCursor(object):
if isinstance(query, unicode):
query = query.encode(db.unicode_literal.charset, 'surrogateescape')
res = None
try:
r = None
r = self._query(query)
res = self._query(query)
except TypeError as m:
if m.args[0] in ("not enough arguments for format string",
"not all arguments converted"):
self.errorhandler(self, ProgrammingError, m.args[0])
else:
self.errorhandler(self, TypeError, m)
except (SystemExit, KeyboardInterrupt):
raise
except:
except Exception:
exc, value = sys.exc_info()[:2]
self.errorhandler(self, exc, value)
self._executed = query
if not self._defer_warnings: self._warning_check()
return r
return res
def executemany(self, query, args):
"""Execute a multi-row query.
query -- string, query to execute on server
args
Sequence of sequences or mappings, parameters to use with
query.
Returns long integer rows affected, if any.
This method improves performance on multiple-row INSERT and
REPLACE. Otherwise it is equivalent to looping over args with
execute().
......@@ -279,11 +285,10 @@ class BaseCursor(object):
r = self._query(qs)
if not self._defer_warnings: self._warning_check()
return r
def callproc(self, procname, args=()):
def callproc(self, procname, args=()):
"""Execute stored procedure procname with args
procname -- string, name of procedure to execute on server
args -- Sequence of parameters to use with procedure
......@@ -318,7 +323,7 @@ class BaseCursor(object):
q = q.encode(db.unicode_literal.charset)
self._query(q)
self.nextset()
q = "CALL %s(%s)" % (procname,
','.join(['@_%s_%d' % (procname, i)
for i in range(len(args))]))
......@@ -326,9 +331,10 @@ class BaseCursor(object):
q = q.encode(db.unicode_literal.charset)
self._query(q)
self._executed = q
if not self._defer_warnings: self._warning_check()
if not self._defer_warnings:
self._warning_check()
return args
def _do_query(self, q):
db = self._get_db()
self._last_executed = q
......@@ -336,8 +342,9 @@ class BaseCursor(object):
self._do_get_result()
return self.rowcount
def _query(self, q): return self._do_query(q)
def _query(self, q):
return self._do_query(q)
def _fetch_row(self, size=1):
if not self._result:
return ()
......@@ -356,7 +363,7 @@ class BaseCursor(object):
InternalError = InternalError
ProgrammingError = ProgrammingError
NotSupportedError = NotSupportedError
class CursorStoreResultMixIn(object):
......@@ -403,11 +410,11 @@ class CursorStoreResultMixIn(object):
result = self._rows
self.rownumber = len(self._rows)
return result
def scroll(self, value, mode='relative'):
"""Scroll the cursor in the result set to a new position according
to mode.
If mode is 'relative' (default), value is taken as offset to
the current position in the result set, if set to 'absolute',
value states an absolute target position."""
......@@ -427,7 +434,7 @@ class CursorStoreResultMixIn(object):
self._check_executed()
result = self.rownumber and self._rows[self.rownumber:] or self._rows
return iter(result)
class CursorUseResultMixIn(object):
......@@ -438,7 +445,7 @@ class CursorUseResultMixIn(object):
the connection."""
_defer_warnings = True
def _get_result(self): return self._get_db().use_result()
def fetchone(self):
......@@ -450,7 +457,7 @@ class CursorUseResultMixIn(object):
return None
self.rownumber = self.rownumber + 1
return r[0]
def fetchmany(self, size=None):
"""Fetch up to size rows from the cursor. Result set may be smaller
than size. If size is not defined, cursor.arraysize is used."""
......@@ -460,7 +467,7 @@ class CursorUseResultMixIn(object):
if not r:
self._warning_check()
return r
def fetchall(self):
"""Fetchs all available rows from the cursor."""
self._check_executed()
......@@ -477,10 +484,11 @@ class CursorUseResultMixIn(object):
if row is None:
raise StopIteration
return row
class CursorTupleRowsMixIn(object):
__next__ = next
class CursorTupleRowsMixIn(object):
"""This is a MixIn class that causes all rows to be returned as tuples,
which is the standard form required by DB API."""
......@@ -488,7 +496,6 @@ class CursorTupleRowsMixIn(object):
class CursorDictRowsMixIn(object):
"""This is a MixIn class that causes all rows to be returned as
dictionaries. This is a non-standard feature."""
......@@ -520,7 +527,6 @@ class CursorDictRowsMixIn(object):
class CursorOldDictRowsMixIn(CursorDictRowsMixIn):
"""This is a MixIn class that returns rows as dictionaries with
the same key convention as the old Mysqldb (MySQLmodule). Don't
use this."""
......@@ -530,28 +536,24 @@ class CursorOldDictRowsMixIn(CursorDictRowsMixIn):
class Cursor(CursorStoreResultMixIn, CursorTupleRowsMixIn,
BaseCursor):
"""This is the standard Cursor class that returns rows as tuples
and stores the result set in the client."""
class DictCursor(CursorStoreResultMixIn, CursorDictRowsMixIn,
BaseCursor):
"""This is a Cursor class that returns rows as dictionaries and
stores the result set in the client."""
class SSCursor(CursorUseResultMixIn, CursorTupleRowsMixIn,
BaseCursor):
"""This is a Cursor class that returns rows as tuples and stores
the result set in the server."""
class SSDictCursor(CursorUseResultMixIn, CursorDictRowsMixIn,
BaseCursor):
"""This is a Cursor class that returns rows as dictionaries and
stores the result set in the server."""
......
__author__ = "Andy Dustman <farcepest@gmail.com>"
version_info = (1,3,6,'final',1)
__version__ = "1.3.6"
version_info = (1,3,7,'final',1)
__version__ = "1.3.7"
......@@ -48,25 +48,32 @@ def format_TIMESTAMP(d):
def DateTime_or_None(s):
if ' ' in s:
sep = ' '
elif 'T' in s:
sep = 'T'
else:
return Date_or_None(s)
try:
d, t = s.split(sep, 1)
if '.' in t:
t, ms = t.split('.',1)
ms = ms.ljust(6, '0')
if len(s) < 11:
return Date_or_None(s)
micros = s[20:]
if len(micros) == 0:
# 12:00:00
micros = 0
elif len(micros) < 7:
# 12:00:00.123456
micros = int(micros) * 10 ** (6 - len(micros))
else:
ms = 0
return datetime(*[ int(x) for x in d.split('-')+t.split(':')+[ms] ])
except (SystemExit, KeyboardInterrupt):
raise
except:
return Date_or_None(s)
return None
return datetime(
int(s[:4]), # year
int(s[5:7]), # month
int(s[8:10]), # day
int(s[11:13] or 0), # hour
int(s[14:16] or 0), # minute
int(s[17:19] or 0), # second
micros, # microsecond
)
except ValueError:
return None
def TimeDelta_or_None(s):
try:
......@@ -107,14 +114,18 @@ def Time_or_None(s):
def Date_or_None(s):
try:
return date(*[ int(x) for x in s.split('-',2)])
except (TypeError, ValueError):
return date(
int(s[:4]), # year
int(s[5:7]), # month
int(s[8:10]), # day
)
except ValueError:
return None
def DateTime2literal(d, c):
"""Format a DateTime object as an ISO timestamp."""
return string_literal(format_TIMESTAMP(d), c)
def DateTimeDelta2literal(d, c):
"""Format a DateTimeDelta object as a time."""
return string_literal(format_TIMEDELTA(d),c)
......@@ -129,6 +140,6 @@ def mysql_timestamp_converter(s):
try:
return Timestamp(*parts)
except (SystemExit, KeyboardInterrupt):
raise
raise # pragma: no cover
except:
return None
Metadata-Version: 1.1
Name: mysqlclient
Version: 1.3.6
Version: 1.3.7
Summary: Python interface to MySQL
Home-page: https://github.com/PyMySQL/mysqlclient-python
Author: INADA Naoki
......@@ -11,7 +11,7 @@ Description:
Python interface to MySQL
=========================
mysqlclient is a fork of MySQL-python. It add Python 3.3 support
mysqlclient is a fork of MySQL-python. It adds Python 3.3~ support
and merges some pull requests.
MySQLdb is an interface to the popular MySQL_ database server for
......@@ -21,7 +21,7 @@ Description:
- Thread-safety
- Thread-friendliness (threads will not block each other)
MySQL-3.23 through 5.5 and Python-2.7, 3.3-3.4 are currently
MySQL-4.1 through 5.5 and Python-2.7, 3.3-3.5 are currently
supported. PyPy is supported.
MySQLdb is `Free Software`_.
......@@ -46,5 +46,6 @@ Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Topic :: Database
Classifier: Topic :: Database :: Database Engines/Servers
mysqlclient
===========
# mysqlclient
[![Build Status](https://secure.travis-ci.org/PyMySQL/mysqlclient-python.png)](http://travis-ci.org/PyMySQL/mysqlclient-python)
......@@ -8,11 +7,35 @@ This is a fork of [MySQLdb1](https://github.com/farcepest/MySQLdb1).
This project adds Python 3 support and bug fixes.
I hope this fork is merged back to MySQLdb1 like distribute was merged back to setuptools.
Prerequisites for Installing
----------------------------
## Install
### Prerequisites
You may need to install the Python and MySQL development headers and libraries like so:
`sudo apt-get install python-dev libmysqlclient-dev` # Debian / Ubuntu
`sudo yum install python-devel mysql-devel` # Red Hat / CentOS
On Windows, there are binary wheel you can install without MySQLConnector/C or MSVC.
#### Note on Python 3 : if you are using python3 then you need to install python3-dev using the following command :
`sudo apt-get install python3-dev` # debian / Ubuntu
`sudo yum install python3-devel ` # Red Hat / CentOS
### Install from PyPI
`pip install mysqlclient`
NOTE: Wheels for Windows may be not released with source package. You should pin version
in your `requirements.txt` to avoid trying install source newest pacakge.
### Install from source
1. Download source by `git clone` or [zipfile](https://github.com/PyMySQL/mysqlclient-python/archive/master.zip).
2. Customize `site.cfg`
3. `python setup.py install`
......@@ -68,7 +68,7 @@ static PyObject *_mysql_IntegrityError;
static PyObject *_mysql_InternalError;
static PyObject *_mysql_ProgrammingError;
static PyObject *_mysql_NotSupportedError;
typedef struct {
PyObject_HEAD
MYSQL connection;
......@@ -88,6 +88,7 @@ typedef struct {
MYSQL_RES *result;
int nfields;
int use;
char has_next;
PyObject *converter;
} _mysql_ResultObject;
......@@ -193,6 +194,9 @@ _mysql_Exception(_mysql_ConnectionObject *c)
#endif
#ifdef ER_CANNOT_ADD_FOREIGN
case ER_CANNOT_ADD_FOREIGN:
#endif
#ifdef ER_NO_DEFAULT_FOR_FIELD
case ER_NO_DEFAULT_FOR_FIELD:
#endif
e = _mysql_IntegrityError;
break;
......@@ -223,7 +227,7 @@ _mysql_Exception(_mysql_ConnectionObject *c)
Py_DECREF(t);
return NULL;
}
static char _mysql_server_init__doc__[] =
"Initialize embedded server. If this client is not linked against\n\
the embedded server library, this function does nothing.\n\
......@@ -246,7 +250,7 @@ static PyObject *_mysql_server_init(
"already initialized");
return NULL;
}
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO", kwlist,
&cmd_args, &groups))
return NULL;
......@@ -345,7 +349,7 @@ static PyObject *_mysql_server_end(
}
return _mysql_Exception(NULL);
}
#if MYSQL_VERSION_ID >= 32314
static char _mysql_thread_safe__doc__[] =
"Indicates whether the client is compiled as thread-safe.";
......@@ -403,9 +407,10 @@ _mysql_ResultObject_Initialize(
else
result = mysql_store_result(&(conn->connection));
self->result = result;
self->has_next = (char)mysql_more_results(&(conn->connection));
Py_END_ALLOW_THREADS ;
if (!result) {
if (mysql_field_count(&(conn->connection)) > 0) {
if (mysql_errno(&(conn->connection))) {
_mysql_Exception(conn);
return -1;
}
......@@ -552,7 +557,7 @@ _mysql_ConnectionObject_Initialize(
char *init_command=NULL,
*read_default_file=NULL,
*read_default_group=NULL;
self->converter = NULL;
self->open = 0;
check_server_init(-1);
......@@ -736,7 +741,7 @@ _mysql_connect(
PyObject *kwargs)
{
_mysql_ConnectionObject *c=NULL;
c = MyAlloc(_mysql_ConnectionObject, _mysql_ConnectionObject_Type);
if (c == NULL) return NULL;
if (_mysql_ConnectionObject_Initialize(c, args, kwargs)) {
......@@ -1286,7 +1291,7 @@ _mysql_escape_dict(
Py_XDECREF(r);
return NULL;
}
static char _mysql_ResultObject_describe__doc__[] =
"Returns the sequence of 7-tuples required by the DB-API for\n\
the Cursor.description attribute.\n\
......@@ -1323,7 +1328,7 @@ _mysql_ResultObject_describe(
Py_XDECREF(d);
return NULL;
}
static char _mysql_ResultObject_field_flags__doc__[] =
"Returns a tuple of field flags, one for each column in the result.\n\
" ;
......@@ -1369,7 +1374,9 @@ _mysql_field_to_python(
field_type == FIELD_TYPE_LONG_BLOB ||
field_type == FIELD_TYPE_BLOB ||
field_type == FIELD_TYPE_VAR_STRING ||
field_type == FIELD_TYPE_STRING) {
field_type == FIELD_TYPE_STRING ||
field_type == FIELD_TYPE_GEOMETRY ||
field_type == FIELD_TYPE_BIT) {
binary = 1;
}
#endif
......@@ -1515,7 +1522,7 @@ _mysql__fetch_row(
int maxrows,
_PYFUNC *convert_row)
{
unsigned int i;
int i;
MYSQL_ROW row;
for (i = skiprows; i<(skiprows+maxrows); i++) {
......@@ -1568,14 +1575,14 @@ _mysql_ResultObject_fetch_row(
_mysql_row_to_dict_old
};
_PYFUNC *convert_row;
unsigned int maxrows=1, how=0, skiprows=0, rowsadded;
int maxrows=1, how=0, skiprows=0, rowsadded;
PyObject *r=NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii:fetch_row", kwlist,
&maxrows, &how))
return NULL;
check_result_connection(self);
if (how >= sizeof(row_converters)) {
if (how >= (int)sizeof(row_converters)) {
PyErr_SetString(PyExc_ValueError, "how out of range");
return NULL;
}
......@@ -1729,7 +1736,7 @@ _mysql_ConnectionObject_get_character_set_info(
{
PyObject *result;
MY_CHARSET_INFO cs;
if (!PyArg_ParseTuple(args, "")) return NULL;
check_connection(self);
mysql_get_character_set_info(&(self->connection), &cs);
......@@ -2648,6 +2655,13 @@ static struct PyMemberDef _mysql_ResultObject_memberlist[] = {
READONLY,
"Type conversion mapping"
},
{
"has_next",
T_BOOL,
offsetof(_mysql_ResultObject, has_next),
READONLY,
"Has next result"
},
{NULL} /* Sentinel */
};
......@@ -2712,44 +2726,44 @@ PyTypeObject _mysql_ConnectionObject_Type = {
0, /* tp_setattr */
0, /*tp_compare*/
(reprfunc)_mysql_ConnectionObject_repr, /* tp_repr */
/* Method suites for standard classes */
0, /* (PyNumberMethods *) tp_as_number */
0, /* (PySequenceMethods *) tp_as_sequence */
0, /* (PyMappingMethods *) tp_as_mapping */
/* More standard operations (here for binary compatibility) */
0, /* (hashfunc) tp_hash */
0, /* (ternaryfunc) tp_call */
0, /* (reprfunc) tp_str */
(getattrofunc)_mysql_ConnectionObject_getattro, /* tp_getattro */
(setattrofunc)_mysql_ConnectionObject_setattro, /* tp_setattro */
/* Functions to access object as input/output buffer */
0, /* (PyBufferProcs *) tp_as_buffer */
/* (tp_flags) Flags to define presence of optional/expanded features */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE,
_mysql_connect__doc__, /* (char *) tp_doc Documentation string */
/* call function for all accessible objects */
(traverseproc) _mysql_ConnectionObject_traverse, /* tp_traverse */
/* delete references to contained objects */
(inquiry) _mysql_ConnectionObject_clear, /* tp_clear */
/* rich comparisons */
0, /* (richcmpfunc) tp_richcompare */
/* weak reference enabler */
0, /* (long) tp_weaklistoffset */
/* Iterators */
0, /* (getiterfunc) tp_iter */
0, /* (iternextfunc) tp_iternext */
/* Attribute descriptor and subclassing stuff */
(struct PyMethodDef *)_mysql_ConnectionObject_methods, /* tp_methods */
(struct PyMemberDef *)_mysql_ConnectionObject_memberlist, /* tp_members */
......@@ -2784,45 +2798,45 @@ PyTypeObject _mysql_ResultObject_Type = {
0, /* tp_setattr */
0, /*tp_compare*/
(reprfunc)_mysql_ResultObject_repr, /* tp_repr */
/* Method suites for standard classes */
0, /* (PyNumberMethods *) tp_as_number */
0, /* (PySequenceMethods *) tp_as_sequence */
0, /* (PyMappingMethods *) tp_as_mapping */
/* More standard operations (here for binary compatibility) */
0, /* (hashfunc) tp_hash */
0, /* (ternaryfunc) tp_call */
0, /* (reprfunc) tp_str */
(getattrofunc)PyObject_GenericGetAttr, /* tp_getattro */
(setattrofunc)_mysql_ResultObject_setattro, /* tp_setattr */
/* Functions to access object as input/output buffer */
0, /* (PyBufferProcs *) tp_as_buffer */
/* Flags to define presence of optional/expanded features */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE,
_mysql_ResultObject__doc__, /* (char *) tp_doc Documentation string */
/* call function for all accessible objects */
(traverseproc) _mysql_ResultObject_traverse, /* tp_traverse */
/* delete references to contained objects */
(inquiry) _mysql_ResultObject_clear, /* tp_clear */
/* rich comparisons */
0, /* (richcmpfunc) tp_richcompare */
/* weak reference enabler */
0, /* (long) tp_weaklistoffset */
/* Iterators */
0, /* (getiterfunc) tp_iter */
0, /* (iternextfunc) tp_iternext */
/* Attribute descriptor and subclassing stuff */
(struct PyMethodDef *) _mysql_ResultObject_methods, /* tp_methods */
(struct PyMemberDef *) _mysql_ResultObject_memberlist, /*tp_members */
......
[metadata]
version: 1.3.6
version_info: (1,3,6,'final',1)
version: 1.3.7
version_info: (1,3,7,'final',1)
description: Python interface to MySQL
long_description:
=========================
Python interface to MySQL
=========================
\n
mysqlclient is a fork of MySQL-python. It add Python 3.3 support
mysqlclient is a fork of MySQL-python. It adds Python 3.3~ support
and merges some pull requests.
\n
MySQLdb is an interface to the popular MySQL_ database server for
......@@ -17,7 +17,7 @@ long_description:
- Thread-safety
- Thread-friendliness (threads will not block each other)
\n
MySQL-3.23 through 5.5 and Python-2.7, 3.3-3.4 are currently
MySQL-4.1 through 5.5 and Python-2.7, 3.3-3.5 are currently
supported. PyPy is supported.
\n
MySQLdb is `Free Software`_.
......@@ -49,6 +49,7 @@ classifiers:
Programming Language :: Python :: 3
Programming Language :: Python :: 3.3
Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5
Topic :: Database
Topic :: Database :: Database Engines/Servers
py_modules:
......
Metadata-Version: 1.1
Name: mysqlclient
Version: 1.3.6
Version: 1.3.7
Summary: Python interface to MySQL
Home-page: https://github.com/PyMySQL/mysqlclient-python
Author: INADA Naoki
......@@ -11,7 +11,7 @@ Description:
Python interface to MySQL
=========================
mysqlclient is a fork of MySQL-python. It add Python 3.3 support
mysqlclient is a fork of MySQL-python. It adds Python 3.3~ support
and merges some pull requests.
MySQLdb is an interface to the popular MySQL_ database server for
......@@ -21,7 +21,7 @@ Description:
- Thread-safety
- Thread-friendliness (threads will not block each other)
MySQL-3.23 through 5.5 and Python-2.7, 3.3-3.4 are currently
MySQL-4.1 through 5.5 and Python-2.7, 3.3-3.5 are currently
supported. PyPy is supported.
MySQLdb is `Free Software`_.
......@@ -46,5 +46,6 @@ Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Topic :: Database
Classifier: Topic :: Database :: Database Engines/Servers
......@@ -38,11 +38,11 @@ doc/user_guide.rst
mysqlclient.egg-info/PKG-INFO
mysqlclient.egg-info/SOURCES.txt
mysqlclient.egg-info/dependency_links.txt
mysqlclient.egg-info/pbr.json
mysqlclient.egg-info/top_level.txt
tests/capabilities.py
tests/configdb.py
tests/dbapi20.py
tests/test_MySQLdb_capabilities.py
tests/test_MySQLdb_dbapi20.py
tests/test_MySQLdb_nonstandard.py
\ No newline at end of file
tests/test_MySQLdb_nonstandard.py
tests/test_MySQLdb_times.py
\ No newline at end of file
{"is_release": false, "git_version": "4481bdb"}
\ No newline at end of file
......@@ -13,6 +13,6 @@ build-requires = python-devel mysql-devel zlib-devel openssl-devel
[egg_info]
tag_date = 0
tag_build =
tag_svn_revision = 0
tag_build =
......@@ -13,9 +13,6 @@ def dequote(s):
s = s[1:-1]
return s
def compiler_flag(f):
return "-%s" % f
def mysql_config(what):
from os import popen
......@@ -53,29 +50,24 @@ def get_config():
libs = mysql_config("libs")
client = "mysqlclient"
library_dirs = [ dequote(i[2:]) for i in libs if i.startswith(compiler_flag("L")) ]
libraries = [ dequote(i[2:]) for i in libs if i.startswith(compiler_flag("l")) ]
library_dirs = [dequote(i[2:]) for i in libs if i.startswith('-L')]
libraries = [dequote(i[2:]) for i in libs if i.startswith('-l')]
extra_link_args = [x for x in libs if not x.startswith(('-l', '-L'))]
removable_compile_args = [ compiler_flag(f) for f in "ILl" ]
extra_compile_args = [ i.replace("%", "%%") for i in mysql_config("cflags")
if i[:2] not in removable_compile_args ]
removable_compile_args = ('-I', '-L', '-l')
extra_compile_args = [i.replace("%", "%%") for i in mysql_config("cflags")
if i[:2] not in removable_compile_args]
# Copy the arch flags for linking as well
extra_link_args = list()
for i in range(len(extra_compile_args)):
if extra_compile_args[i] == '-arch':
extra_link_args += ['-arch', extra_compile_args[i + 1]]
include_dirs = [ dequote(i[2:])
for i in mysql_config('include')
if i.startswith(compiler_flag('I')) ]
if not include_dirs: # fix for MySQL-3.23
include_dirs = [ dequote(i[2:])
for i in mysql_config('cflags')
if i.startswith(compiler_flag('I')) ]
include_dirs = [dequote(i[2:])
for i in mysql_config('include') if i.startswith('-I')]
if static:
extra_objects.append(os.path.join(library_dirs[0],'lib%s.a' % client))
extra_objects.append(os.path.join(library_dirs[0], 'lib%s.a' % client))
if client in libraries:
libraries.remove(client)
......@@ -104,4 +96,3 @@ def get_config():
if __name__ == "__main__":
sys.stderr.write("""You shouldn't be running this directly; it is used by setup.py.""")
......@@ -10,16 +10,15 @@ connect_kwargs = dict(
read_default_group = "MySQLdb-tests",
)
def connection_kwargs(kwargs):
db_kwargs = connect_kwargs.copy()
db_kwargs.update(kwargs)
return db_kwargs
def connection_factory(**kwargs):
import MySQLdb
db_kwargs = connection_kwargs(kwargs)
db = MySQLdb.connect(**db_kwargs)
return db
#!/usr/bin/env python
import capabilities
from datetime import timedelta
import unittest
import MySQLdb
import warnings
warnings.filterwarnings('ignore')
class test_MySQLdb(capabilities.DatabaseTest):
db_module = MySQLdb
......@@ -13,12 +16,11 @@ class test_MySQLdb(capabilities.DatabaseTest):
connect_kwargs = dict(use_unicode=True, sql_mode="ANSI,STRICT_TRANS_TABLES,TRADITIONAL")
create_table_extra = "ENGINE=INNODB CHARACTER SET UTF8"
leak_test = False
def quote_identifier(self, ident):
return "`%s`" % ident
def test_TIME(self):
from datetime import timedelta
def generator(row,col):
return timedelta(0, row*8000)
self.check_data_integrity(
......@@ -27,7 +29,7 @@ class test_MySQLdb(capabilities.DatabaseTest):
def test_TINYINT(self):
# Number data
def generator(row,col):
def generator(row, col):
v = (row*row) % 256
if v > 127:
v = v-256
......@@ -35,7 +37,7 @@ class test_MySQLdb(capabilities.DatabaseTest):
self.check_data_integrity(
('col1 TINYINT',),
generator)
def test_stored_procedures(self):
db = self.connection
c = self.cursor
......@@ -43,7 +45,7 @@ class test_MySQLdb(capabilities.DatabaseTest):
c.executemany("INSERT INTO %s (pos,tree) VALUES (%%s,%%s)" % self.table,
list(enumerate('ash birch cedar larch pine'.split())))
db.commit()
c.execute("""
CREATE PROCEDURE test_sp(IN t VARCHAR(255))
BEGIN
......@@ -57,7 +59,7 @@ class test_MySQLdb(capabilities.DatabaseTest):
self.assertEquals(len(rows), 1)
self.assertEquals(rows[0][0], 3)
c.nextset()
c.execute("DROP PROCEDURE test_sp")
c.execute('drop table %s' % (self.table))
......@@ -71,7 +73,58 @@ class test_MySQLdb(capabilities.DatabaseTest):
self.check_data_integrity(
('col1 char(1)','col2 char(1)'),
generator)
def test_BIT(self):
c = self.cursor
try:
c.execute("""create table test_BIT (
b3 BIT(3),
b7 BIT(10),
b64 BIT(64))""")
one64 = '1'*64
c.execute(
"insert into test_BIT (b3, b7, b64)"
" VALUES (b'011', b'1111111111', b'%s')"
% one64)
c.execute("SELECT b3, b7, b64 FROM test_BIT")
row = c.fetchone()
self.assertEqual(row[0], b'\x03')
self.assertEqual(row[1], b'\x03\xff')
self.assertEqual(row[2], b'\xff'*8)
finally:
c.execute("drop table if exists test_BIT")
def test_MULTIPOLYGON(self):
c = self.cursor
try:
c.execute("""create table test_MULTIPOLYGON (
id INTEGER PRIMARY KEY,
border MULTIPOLYGON)""")
c.execute(
"insert into test_MULTIPOLYGON (id, border)"
" VALUES (1, GeomFromText('MULTIPOLYGON(((1 1, 1 -1, -1 -1, -1 1, 1 1)),((1 1, 3 1, 3 3, 1 3, 1 1)))'))"
)
c.execute("SELECT id, AsText(border) FROM test_MULTIPOLYGON")
row = c.fetchone()
self.assertEqual(row[0], 1)
self.assertEqual(row[1], b'MULTIPOLYGON(((1 1,1 -1,-1 -1,-1 1,1 1)),((1 1,3 1,3 3,1 3,1 1)))')
c.execute("SELECT id, AsWKB(border) FROM test_MULTIPOLYGON")
row = c.fetchone()
self.assertEqual(row[0], 1)
self.assertNotEqual(len(row[1]), 0)
c.execute("SELECT id, border FROM test_MULTIPOLYGON")
row = c.fetchone()
self.assertEqual(row[0], 1)
self.assertNotEqual(len(row[1]), 0)
finally:
c.execute("drop table if exists test_MULTIPOLYGON")
def test_bug_2671682(self):
from MySQLdb.constants import ER
try:
......@@ -102,7 +155,7 @@ class test_MySQLdb(capabilities.DatabaseTest):
return
self.fail("Should raise ProgrammingError")
if __name__ == '__main__':
if test_MySQLdb.leak_test:
import gc
......
#!/usr/bin/env python
import dbapi20
import unittest
import MySQLdb
from configdb import connection_kwargs
import warnings
warnings.simplefilter("ignore")
class test_MySQLdb(dbapi20.DatabaseAPI20Test):
driver = MySQLdb
connect_args = ()
connect_kw_args = connection_kwargs(dict(sql_mode="ANSI,STRICT_TRANS_TABLES,TRADITIONAL"))
def test_setoutputsize(self): pass
def test_setoutputsize_basic(self): pass
def test_nextset(self): pass
"""The tests on fetchone and fetchall and rowcount bogusly
test for an exception if the statement cannot return a
result set. MySQL always returns a result set; it's just that
some things return empty result sets."""
def test_fetchall(self):
con = self._connect()
try:
cur = con.cursor()
# cursor.fetchall should raise an Error if called
# without executing a query that may return rows (such
# as a select)
self.assertRaises(self.driver.Error, cur.fetchall)
self.executeDDL1(cur)
for sql in self._populate():
cur.execute(sql)
# cursor.fetchall should raise an Error if called
# after executing a a statement that cannot return rows
## self.assertRaises(self.driver.Error,cur.fetchall)
cur.execute('select name from %sbooze' % self.table_prefix)
rows = cur.fetchall()
self.assertTrue(cur.rowcount in (-1,len(self.samples)))
self.assertEqual(len(rows),len(self.samples),
'cursor.fetchall did not retrieve all rows'
)
rows = [r[0] for r in rows]
rows.sort()
for i in range(0,len(self.samples)):
self.assertEqual(rows[i],self.samples[i],
'cursor.fetchall retrieved incorrect rows'
)
rows = cur.fetchall()
self.assertEqual(
len(rows),0,
'cursor.fetchall should return an empty list if called '
'after the whole result set has been fetched'
)
self.assertTrue(cur.rowcount in (-1,len(self.samples)))
self.executeDDL2(cur)
cur.execute('select name from %sbarflys' % self.table_prefix)
rows = cur.fetchall()
self.assertTrue(cur.rowcount in (-1,0))
self.assertEqual(len(rows),0,
'cursor.fetchall should return an empty list if '
'a select query returns no rows'
)
finally:
con.close()
def test_fetchone(self):
con = self._connect()
try:
cur = con.cursor()
# cursor.fetchone should raise an Error if called before
# executing a select-type query
self.assertRaises(self.driver.Error,cur.fetchone)
# cursor.fetchone should raise an Error if called after
# executing a query that cannnot return rows
self.executeDDL1(cur)
## self.assertRaises(self.driver.Error,cur.fetchone)
cur.execute('select name from %sbooze' % self.table_prefix)
self.assertEqual(cur.fetchone(),None,
'cursor.fetchone should return None if a query retrieves '
'no rows'
)
self.assertTrue(cur.rowcount in (-1,0))
# cursor.fetchone should raise an Error if called after
# executing a query that cannnot return rows
cur.execute("insert into %sbooze values ('Victoria Bitter')" % (
self.table_prefix
))
## self.assertRaises(self.driver.Error,cur.fetchone)
cur.execute('select name from %sbooze' % self.table_prefix)
r = cur.fetchone()
self.assertEqual(len(r),1,
'cursor.fetchone should have retrieved a single row'
)
self.assertEqual(r[0],'Victoria Bitter',
'cursor.fetchone retrieved incorrect data'
)
## self.assertEqual(cur.fetchone(),None,
## 'cursor.fetchone should return None if no more rows available'
## )
self.assertTrue(cur.rowcount in (-1,1))
finally:
con.close()
# Same complaint as for fetchall and fetchone
def test_rowcount(self):
con = self._connect()
try:
cur = con.cursor()
self.executeDDL1(cur)
## self.assertEqual(cur.rowcount,-1,
## 'cursor.rowcount should be -1 after executing no-result '
## 'statements'
## )
cur.execute("insert into %sbooze values ('Victoria Bitter')" % (
self.table_prefix
))
## self.assertTrue(cur.rowcount in (-1,1),
## 'cursor.rowcount should == number or rows inserted, or '
## 'set to -1 after executing an insert statement'
## )
cur.execute("select name from %sbooze" % self.table_prefix)
self.assertTrue(cur.rowcount in (-1,1),
'cursor.rowcount should == number of rows returned, or '
'set to -1 after executing a select statement'
)
self.executeDDL2(cur)
## self.assertEqual(cur.rowcount,-1,
## 'cursor.rowcount not being reset to -1 after executing '
## 'no-result statements'
## )
finally:
con.close()
def test_callproc(self):
pass # performed in test_MySQL_capabilities
def help_nextset_setUp(self,cur):
''' Should create a procedure called deleteme
that returns two result sets, first the
number of rows in booze then "name from booze"
'''
sql="""
create procedure deleteme()
begin
select count(*) from %(tp)sbooze;
select name from %(tp)sbooze;
end
""" % dict(tp=self.table_prefix)
cur.execute(sql)
def help_nextset_tearDown(self,cur):
'If cleaning up is needed after nextSetTest'
cur.execute("drop procedure deleteme")
def test_nextset(self):
from warnings import warn
con = self._connect()
try:
cur = con.cursor()
if not hasattr(cur,'nextset'):
return
try:
self.executeDDL1(cur)
sql=self._populate()
for sql in self._populate():
cur.execute(sql)
self.help_nextset_setUp(cur)
cur.callproc('deleteme')
numberofrows=cur.fetchone()
assert numberofrows[0]== len(self.samples)
assert cur.nextset()
names=cur.fetchall()
assert len(names) == len(self.samples)
s=cur.nextset()
if s:
empty = cur.fetchall()
self.assertEquals(len(empty), 0,
"non-empty result set after other result sets")
#warn("Incompatibility: MySQL returns an empty result set for the CALL itself",
# Warning)
#assert s == None,'No more return sets, should return None'
finally:
self.help_nextset_tearDown(cur)
finally:
con.close()
if __name__ == '__main__':
unittest.main()
#!/usr/bin/env python
import dbapi20
import unittest
import MySQLdb
from configdb import connection_kwargs
import warnings
warnings.simplefilter("ignore")
class test_MySQLdb(dbapi20.DatabaseAPI20Test):
driver = MySQLdb
connect_args = ()
connect_kw_args = connection_kwargs(dict(sql_mode="ANSI,STRICT_TRANS_TABLES,TRADITIONAL"))
def test_setoutputsize(self): pass
def test_setoutputsize_basic(self): pass
def test_nextset(self): pass
"""The tests on fetchone and fetchall and rowcount bogusly
test for an exception if the statement cannot return a
result set. MySQL always returns a result set; it's just that
some things return empty result sets."""
def test_fetchall(self):
con = self._connect()
try:
cur = con.cursor()
# cursor.fetchall should raise an Error if called
# without executing a query that may return rows (such
# as a select)
self.assertRaises(self.driver.Error, cur.fetchall)
self.executeDDL1(cur)
for sql in self._populate():
cur.execute(sql)
# cursor.fetchall should raise an Error if called
# after executing a a statement that cannot return rows
#self.assertRaises(self.driver.Error,cur.fetchall)
cur.execute('select name from %sbooze' % self.table_prefix)
rows = cur.fetchall()
self.assertTrue(cur.rowcount in (-1,len(self.samples)))
self.assertEqual(len(rows),len(self.samples),
'cursor.fetchall did not retrieve all rows'
)
rows = [r[0] for r in rows]
rows.sort()
for i in range(0,len(self.samples)):
self.assertEqual(rows[i],self.samples[i],
'cursor.fetchall retrieved incorrect rows'
)
rows = cur.fetchall()
self.assertEqual(
len(rows),0,
'cursor.fetchall should return an empty list if called '
'after the whole result set has been fetched'
)
self.assertTrue(cur.rowcount in (-1,len(self.samples)))
self.executeDDL2(cur)
cur.execute('select name from %sbarflys' % self.table_prefix)
rows = cur.fetchall()
self.assertTrue(cur.rowcount in (-1,0))
self.assertEqual(len(rows),0,
'cursor.fetchall should return an empty list if '
'a select query returns no rows'
)
finally:
con.close()
def test_fetchone(self):
con = self._connect()
try:
cur = con.cursor()
# cursor.fetchone should raise an Error if called before
# executing a select-type query
self.assertRaises(self.driver.Error,cur.fetchone)
# cursor.fetchone should raise an Error if called after
# executing a query that cannnot return rows
self.executeDDL1(cur)
## self.assertRaises(self.driver.Error,cur.fetchone)
cur.execute('select name from %sbooze' % self.table_prefix)
self.assertEqual(cur.fetchone(),None,
'cursor.fetchone should return None if a query retrieves '
'no rows'
)
self.assertTrue(cur.rowcount in (-1,0))
# cursor.fetchone should raise an Error if called after
# executing a query that cannnot return rows
cur.execute("insert into %sbooze values ('Victoria Bitter')" % (
self.table_prefix
))
## self.assertRaises(self.driver.Error,cur.fetchone)
cur.execute('select name from %sbooze' % self.table_prefix)
r = cur.fetchone()
self.assertEqual(len(r),1,
'cursor.fetchone should have retrieved a single row'
)
self.assertEqual(r[0],'Victoria Bitter',
'cursor.fetchone retrieved incorrect data'
)
## self.assertEqual(cur.fetchone(),None,
## 'cursor.fetchone should return None if no more rows available'
## )
self.assertTrue(cur.rowcount in (-1,1))
finally:
con.close()
# Same complaint as for fetchall and fetchone
def test_rowcount(self):
con = self._connect()
try:
cur = con.cursor()
self.executeDDL1(cur)
## self.assertEqual(cur.rowcount,-1,
## 'cursor.rowcount should be -1 after executing no-result '
## 'statements'
## )
cur.execute("insert into %sbooze values ('Victoria Bitter')" % (
self.table_prefix
))
## self.assertTrue(cur.rowcount in (-1,1),
## 'cursor.rowcount should == number or rows inserted, or '
## 'set to -1 after executing an insert statement'
## )
cur.execute("select name from %sbooze" % self.table_prefix)
self.assertTrue(cur.rowcount in (-1,1),
'cursor.rowcount should == number of rows returned, or '
'set to -1 after executing a select statement'
)
self.executeDDL2(cur)
## self.assertEqual(cur.rowcount,-1,
## 'cursor.rowcount not being reset to -1 after executing '
## 'no-result statements'
## )
finally:
con.close()
def test_callproc(self):
pass # performed in test_MySQL_capabilities
def help_nextset_setUp(self,cur):
''' Should create a procedure called deleteme
that returns two result sets, first the
number of rows in booze then "name from booze"
'''
sql="""
create procedure deleteme()
begin
select count(*) from %(tp)sbooze;
select name from %(tp)sbooze;
end
""" % dict(tp=self.table_prefix)
cur.execute(sql)
def help_nextset_tearDown(self,cur):
'If cleaning up is needed after nextSetTest'
cur.execute("drop procedure deleteme")
def test_nextset(self):
#from warnings import warn
con = self._connect()
try:
cur = con.cursor()
if not hasattr(cur, 'nextset'):
return
try:
self.executeDDL1(cur)
sql=self._populate()
for sql in self._populate():
cur.execute(sql)
self.help_nextset_setUp(cur)
cur.callproc('deleteme')
numberofrows=cur.fetchone()
assert numberofrows[0]== len(self.samples)
assert cur.nextset()
names=cur.fetchall()
assert len(names) == len(self.samples)
s=cur.nextset()
if s:
empty = cur.fetchall()
self.assertEquals(len(empty), 0,
"non-empty result set after other result sets")
#warn("Incompatibility: MySQL returns an empty result set for the CALL itself",
# Warning)
#assert s == None,'No more return sets, should return None'
finally:
self.help_nextset_tearDown(cur)
finally:
con.close()
if __name__ == '__main__':
unittest.main()
......@@ -7,6 +7,7 @@ from configdb import connection_factory
import warnings
warnings.simplefilter("ignore")
class TestDBAPISet(unittest.TestCase):
def test_set_equality(self):
self.assertTrue(MySQLdb.STRING == MySQLdb.STRING)
......@@ -21,7 +22,7 @@ class TestDBAPISet(unittest.TestCase):
self.assertTrue(FIELD_TYPE.DATE != MySQLdb.STRING)
class CoreModule(unittest.TestCase):
class TestCoreModule(unittest.TestCase):
"""Core _mysql module features."""
def test_NULL(self):
......
import mock
import unittest
from time import gmtime
from datetime import time, date, datetime, timedelta
from MySQLdb import times
import warnings
warnings.simplefilter("ignore")
class TestX_or_None(unittest.TestCase):
def test_date_or_none(self):
assert times.Date_or_None('1969-01-01') == date(1969, 1, 1)
assert times.Date_or_None('2015-01-01') == date(2015, 1, 1)
assert times.Date_or_None('2015-12-13') == date(2015, 12, 13)
assert times.Date_or_None('') is None
assert times.Date_or_None('fail') is None
assert times.Date_or_None('2015-12') is None
assert times.Date_or_None('2015-12-40') is None
assert times.Date_or_None('0000-00-00') is None
def test_time_or_none(self):
assert times.Time_or_None('00:00:00') == time(0, 0)
assert times.Time_or_None('01:02:03') == time(1, 2, 3)
assert times.Time_or_None('01:02:03.123456') == time(1, 2, 3, 123456)
assert times.Time_or_None('') is None
assert times.Time_or_None('fail') is None
assert times.Time_or_None('24:00:00') is None
assert times.Time_or_None('01:02:03.123456789') is None
def test_datetime_or_none(self):
assert times.DateTime_or_None('1000-01-01') == date(1000, 1, 1)
assert times.DateTime_or_None('2015-12-13') == date(2015, 12, 13)
assert times.DateTime_or_None('2015-12-13 01:02') == datetime(2015, 12, 13, 1, 2)
assert times.DateTime_or_None('2015-12-13T01:02') == datetime(2015, 12, 13, 1, 2)
assert times.DateTime_or_None('2015-12-13 01:02:03') == datetime(2015, 12, 13, 1, 2, 3)
assert times.DateTime_or_None('2015-12-13T01:02:03') == datetime(2015, 12, 13, 1, 2, 3)
assert times.DateTime_or_None('2015-12-13 01:02:03.123') == datetime(2015, 12, 13, 1, 2, 3, 123000)
assert times.DateTime_or_None('2015-12-13 01:02:03.000123') == datetime(2015, 12, 13, 1, 2, 3, 123)
assert times.DateTime_or_None('2015-12-13 01:02:03.123456') == datetime(2015, 12, 13, 1, 2, 3, 123456)
assert times.DateTime_or_None('2015-12-13T01:02:03.123456') == datetime(2015, 12, 13, 1, 2, 3, 123456)
assert times.DateTime_or_None('') is None
assert times.DateTime_or_None('fail') is None
assert times.DateTime_or_None('0000-00-00 00:00:00') is None
assert times.DateTime_or_None('0000-00-00 00:00:00.000000') is None
assert times.DateTime_or_None('2015-12-13T01:02:03.123456789') is None
def test_timedelta_or_none(self):
assert times.TimeDelta_or_None('-1:0:0') == timedelta(0, -3600)
assert times.TimeDelta_or_None('1:0:0') == timedelta(0, 3600)
assert times.TimeDelta_or_None('12:55:30') == timedelta(0, 46530)
assert times.TimeDelta_or_None('12:55:30.123456') == timedelta(0, 46530, 123456)
assert times.TimeDelta_or_None('12:55:30.123456789') == timedelta(0, 46653, 456789)
assert times.TimeDelta_or_None('12:55:30.123456789123456') == timedelta(1429, 37719, 123456)
assert times.TimeDelta_or_None('') is None
assert times.TimeDelta_or_None('0') is None
assert times.TimeDelta_or_None('fail') is None
class TestTicks(unittest.TestCase):
@mock.patch('MySQLdb.times.localtime', side_effect=gmtime)
def test_date_from_ticks(self, mock):
assert times.DateFromTicks(0) == date(1970, 1, 1)
assert times.DateFromTicks(1430000000) == date(2015, 4, 25)
@mock.patch('MySQLdb.times.localtime', side_effect=gmtime)
def test_time_from_ticks(self, mock):
assert times.TimeFromTicks(0) == time(0, 0, 0)
assert times.TimeFromTicks(1431100000) == time(15, 46, 40)
assert times.TimeFromTicks(1431100000.123) == time(15, 46, 40)
@mock.patch('MySQLdb.times.localtime', side_effect=gmtime)
def test_timestamp_from_ticks(self, mock):
assert times.TimestampFromTicks(0) == datetime(1970, 1, 1, 0, 0, 0)
assert times.TimestampFromTicks(1430000000) == datetime(2015, 4, 25, 22, 13, 20)
assert times.TimestampFromTicks(1430000000.123) == datetime(2015, 4, 25, 22, 13, 20)
class TestTimestampConverter(unittest.TestCase):
def test_mysql_timestamp_converter(self):
assert times.mysql_timestamp_converter('2015-12-13') == date(2015, 12, 13)
assert times.mysql_timestamp_converter('2038-01-19 03:14:07') == datetime(2038, 1, 19, 3, 14, 7)
assert times.mysql_timestamp_converter('2015121310') == datetime(2015, 12, 13, 10, 0)
assert times.mysql_timestamp_converter('20151213101112') == datetime(2015, 12, 13, 10, 11, 12)
assert times.mysql_timestamp_converter('20151313') is None
assert times.mysql_timestamp_converter('2015-13-13') is None
class TestToLiteral(unittest.TestCase):
def test_datetime_to_literal(self):
assert times.DateTime2literal(datetime(2015, 12, 13), '') == b"'2015-12-13 00:00:00'"
assert times.DateTime2literal(datetime(2015, 12, 13, 11, 12, 13), '') == b"'2015-12-13 11:12:13'"
assert times.DateTime2literal(datetime(2015, 12, 13, 11, 12, 13, 123456), '') == b"'2015-12-13 11:12:13.123456'"
def test_datetimedelta_to_literal(self):
d = datetime(2015, 12, 13, 1, 2, 3) - datetime(2015, 12, 13, 1, 2, 2)
assert times.DateTimeDelta2literal(d, '') == b"'0 0:0:1'"
class TestFormat(unittest.TestCase):
def test_format_timedelta(self):
d = datetime(2015, 1, 1) - datetime(2015, 1, 1)
assert times.format_TIMEDELTA(d) == '0 0:0:0'
d = datetime(2015, 1, 1, 10, 11, 12) - datetime(2015, 1, 1, 8, 9, 10)
assert times.format_TIMEDELTA(d) == '0 2:2:2'
d = datetime(2015, 1, 1, 10, 11, 12) - datetime(2015, 1, 1, 11, 12, 13)
assert times.format_TIMEDELTA(d) == '-1 22:58:59'
def test_format_timestamp(self):
assert times.format_TIMESTAMP(datetime(2015, 2, 3)) == '2015-02-03 00:00:00'
assert times.format_TIMESTAMP(datetime(2015, 2, 3, 17, 18, 19)) == '2015-02-03 17:18:19'
assert times.format_TIMESTAMP(datetime(15, 2, 3, 17, 18, 19)) == '0015-02-03 17:18:19'
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment