More major code heaving.

All the threading stuff is ripped out and burned.
Too much code for not enough benefit. Still thread-safe,
just don't share connections.

Made a nice Set class for SET columns.

Updated the docs.
This commit is contained in:
adustman
2001-05-11 05:07:33 +00:00
parent d05c6996da
commit 9c6aaae1eb
8 changed files with 294 additions and 313 deletions

View File

@@ -20,7 +20,7 @@ version_info = (
9,
0,
"beta",
1)
2)
if version_info[3] == "final": __version__ = "%d.%d.%d" % version_info[:3]
else: __version__ = "%d.%d.%d%1.1s%d" % version_info[:5]
@@ -31,46 +31,28 @@ if __version__ != getattr(_mysql, '__version__', None):
(__version__, _mysql.__version__)
threadsafety = 2
threadsafety = 1
apilevel = "2.0"
paramstyle = "format"
class DBAPITypeObject:
"""Helper class for determining column types; required by DB API."""
def __init__(self,*values):
self.values = values
def __cmp__(self,other):
if other in self.values:
return 0
if other < self.values:
return 1
else:
return -1
_Set = DBAPITypeObject
from constants import FIELD_TYPE
STRING = _Set(FIELD_TYPE.CHAR, FIELD_TYPE.ENUM, FIELD_TYPE.INTERVAL,
FIELD_TYPE.SET, FIELD_TYPE.STRING, FIELD_TYPE.VAR_STRING)
BINARY = _Set(FIELD_TYPE.BLOB, FIELD_TYPE.LONG_BLOB, FIELD_TYPE.MEDIUM_BLOB,
FIELD_TYPE.TINY_BLOB)
NUMBER = _Set(FIELD_TYPE.DECIMAL, FIELD_TYPE.DOUBLE, FIELD_TYPE.FLOAT,
FIELD_TYPE.INT24, FIELD_TYPE.LONG, FIELD_TYPE.LONGLONG,
FIELD_TYPE.TINY, FIELD_TYPE.YEAR)
DATE = _Set(FIELD_TYPE.DATE, FIELD_TYPE.NEWDATE)
TIME = _Set(FIELD_TYPE.TIME)
TIMESTAMP = _Set(FIELD_TYPE.TIMESTAMP, FIELD_TYPE.DATETIME)
ROWID = _Set()
def Binary(x): return str(x)
from _mysql import *
from connections import Connection
from converters import *
from constants import FIELD_TYPE
STRING = Set(FIELD_TYPE.CHAR, FIELD_TYPE.ENUM, FIELD_TYPE.STRING,
FIELD_TYPE.VAR_STRING)
BINARY = Set(FIELD_TYPE.BLOB, FIELD_TYPE.LONG_BLOB, FIELD_TYPE.MEDIUM_BLOB,
FIELD_TYPE.TINY_BLOB)
NUMBER = Set(FIELD_TYPE.DECIMAL, FIELD_TYPE.DOUBLE, FIELD_TYPE.FLOAT,
FIELD_TYPE.INT24, FIELD_TYPE.LONG, FIELD_TYPE.LONGLONG,
FIELD_TYPE.TINY, FIELD_TYPE.YEAR)
DATE = Set(FIELD_TYPE.DATE, FIELD_TYPE.NEWDATE)
TIME = Set(FIELD_TYPE.TIME)
TIMESTAMP = Set(FIELD_TYPE.TIMESTAMP, FIELD_TYPE.DATETIME)
ROWID = Set()
def Binary(x): return str(x)
def Connect(*args, **kwargs):
"""Factory function for connections.Connection."""
@@ -78,5 +60,17 @@ def Connect(*args, **kwargs):
connect = Connect
__all__ = ['BINARY', 'Binary', 'Connect', 'Connection', 'DATE', 'DataError', 'DatabaseError', 'Error', 'FIELD_TYPE', 'IntegrityError', 'InterfaceError', 'InternalError', 'MySQLError', 'NULL', 'NUMBER', 'NotSupportedError', 'OperationalError', 'ProgrammingError', 'ROWID', 'STRING', 'TIME', 'TIMESTAMP', 'Warning', 'apilevel', 'connect', 'connections', 'constants', 'cursors', 'debug', 'escape', 'escape_dict', 'escape_sequence', 'escape_string', 'get_client_info', 'paramstyle', 'string_literal', 'threadsafety', 'version_info']
__all__ = [ 'BINARY', 'Binary', 'Connect', 'Connection', 'DATE',
'Date', 'Time', 'Timestamp', 'DateFromTicks', 'TimeFromTicks',
'TimestampFromTicks', 'DataError', 'DatabaseError', 'Error',
'FIELD_TYPE', 'IntegrityError', 'InterfaceError', 'InternalError',
'MySQLError', 'NULL', 'NUMBER', 'NotSupportedError',
'OperationalError', 'ProgrammingError', 'ROWID', 'STRING', 'TIME',
'TIMESTAMP', 'Set', 'Warning', 'apilevel', 'connect', 'connections',
'constants', 'cursors', 'debug', 'escape', 'escape_dict',
'escape_sequence', 'escape_string', 'get_client_info',
'paramstyle', 'string_literal', 'threadsafety', 'version_info']

View File

@@ -33,8 +33,6 @@ class Connection:
read_default_group -- see the MySQL documentation for mysql_options()
cursorclass -- class object, used to create cursors or cursors.Cursor.
This parameter MUST be specified as a keyword parameter.
threads -- boolean, if false threading is disabled, otherwise threads
are enabled by default. (MUST be a keyword parameter.)
Returns a Connection object.
@@ -50,9 +48,6 @@ class Connection:
from converters import conversions
import types
kwargs2 = kwargs.copy()
self.__threads = kwargs.get('threads',1)
if kwargs.has_key('threads'):
del kwargs2['threads']
if not kwargs.has_key('conv'):
kwargs2['conv'] = conversions.copy()
if kwargs.has_key('cursorclass'):
@@ -64,69 +59,9 @@ class Connection:
self._db.converter[types.StringType] = self._db.string_literal
self._transactional = self._db.server_capabilities & CLIENT.TRANSACTIONS
self._autocommit = 1
if self.__threads:
# __c_lock: connection lock. Cursors must always obtain the
# connection lock before doing any queries. A blocking
# call is used. If the same thread tries to acquire the
# lock, produce an error.
#
# _t_lock: transaction lock. If operating transactionally,
# Cursors must acquire the transaction lock on the first
# query. The same thread may acquire the lock more than
# once. commit() or rollback() or an error releases this
# lock.
import threading
self.__c_lock = threading.Lock()
self.__c_locker = None
self.__t_lock = threading.Lock()
self.__t_locker = None
def __del__(self):
self.close()
def _begin(self):
"""Obtain the transaction lock. A thread may try to obtain this
lock multiple times."""
if not self.__threads: return
import threading
me = threading.currentThread()
if self.__t_locker == me: return
self.__t_lock.acquire()
self.__t_locker = me
def _end(self):
"""Release the transaction lock. If a thread tries to release this
lock when it is not currently locking it, it does nothing."""
if not self.__threads: return
import threading
me = threading.currentThread()
if self.__t_locker != me: return
self.__t_locker = None
self.__t_lock.release()
def _acquire(self):
"""Acquire the connection. ProgrammingError is raised if the
thread has already acquired the connection."""
if not self.__threads: return
import threading
me = threading.currentThread()
if self.__c_locker == me:
raise ProgrammingError, "would produce deadlock"
self.__c_lock.acquire()
self.__c_locker = me
def _release(self):
"""Release the connection. If a thread tries to release this
lock when it is not currently locking it, ProgrammingError
is raised (this shouldn't happen)."""
if not self.__threads: return
import threading
me = threading.currentThread()
if self.__c_locker != me:
if not self.__c_locker: return
raise ProgrammingError, "tried to release another %s's lock" % self.__c_locker
self.__c_locker = None
self.__c_lock.release()
if hasattr(self, '_db'): self.close()
def close(self):
"""Close the connection. No further activity possible."""
@@ -149,7 +84,6 @@ class Connection:
if self._transactional:
self._db.query("COMMIT")
finally:
self._end()
self._transactional = not self._autocommit
def rollback(self):
@@ -160,7 +94,6 @@ class Connection:
else:
raise NotSupportedError, "Not supported by server"
finally:
self._end()
self._transactional = not self._autocommit
def cursor(self, cursorclass=None):

View File

@@ -27,9 +27,15 @@ MySQL.connect().
from _mysql import string_literal, escape_sequence, escape_dict, escape, NULL
from constants import FIELD_TYPE
from time import localtime, strftime
from data import *
from string import split
import types
def Str2Set(s):
values = split(s, ',')
return apply(Set, tuple(values))
def Thing2Str(s, d):
"""Convert something into a string via str()."""
return str(s)
@@ -63,8 +69,7 @@ def Instance2Str(o, d):
converter. If the exact class is not found in d, it will use the
first class it can find for which o is an instance."""
if d.has_key(o.__class__): return
d[o.__class__](o, d)
if d.has_key(o.__class__): return d[o.__class__](o, d)
cl = filter(lambda x,o=o:
type(x)==types.ClassType and isinstance(o,x), d.keys())
if not cl:
@@ -82,6 +87,8 @@ conversions = {
types.DictType: escape_dict,
types.InstanceType: Instance2Str,
types.StringType: Thing2Literal, # default
DateTimeType: DateTime2literal,
DateTimeDeltaType: DateTimeDelta2literal,
FIELD_TYPE.TINY: int,
FIELD_TYPE.SHORT: int,
FIELD_TYPE.LONG: long,
@@ -89,99 +96,11 @@ conversions = {
FIELD_TYPE.DOUBLE: float,
FIELD_TYPE.LONGLONG: long,
FIELD_TYPE.INT24: int,
FIELD_TYPE.YEAR: int
FIELD_TYPE.YEAR: int,
FIELD_TYPE.SET: Str2Set,
FIELD_TYPE.TIMESTAMP: mysql_timestamp_converter,
FIELD_TYPE.DATETIME: DateTime_or_None,
FIELD_TYPE.TIME: TimeDelta_or_None,
FIELD_TYPE.DATE: Date_or_None,
}
try:
try:
# new packaging
from mx.DateTime import Date, Time, Timestamp, ISO, \
DateTimeType, DateTimeDeltaType
except ImportError:
# old packaging, deprecated
from DateTime import Date, Time, Timestamp, ISO, \
DateTimeType, DateTimeDeltaType
def DateFromTicks(ticks):
"""Convert UNIX ticks into a mx.DateTime.Date."""
return apply(Date, localtime(ticks)[:3])
def TimeFromTicks(ticks):
"""Convert UNIX ticks into a mx.DateTime.Time."""
return apply(Time, localtime(ticks)[3:6])
def TimestampFromTicks(ticks):
"""Convert UNIX ticks into a mx.DateTime.Timestamp."""
return apply(Timestamp, localtime(ticks)[:6])
def format_DATE(d):
"""Format a DateTime object as an ISO date."""
return d.strftime("%Y-%m-%d")
def format_TIME(d):
"""Format a DateTime object as a time value."""
return d.strftime("%H:%M:%S")
def format_TIMESTAMP(d):
"""Format a DateTime object as an ISO timestamp."""
return d.strftime("%Y-%m-%d %H:%M:%S")
def mysql_timestamp_converter(s):
"""Convert a MySQL TIMESTAMP to a mx.DateTime.Timestamp."""
parts = map(int, filter(None, (s[:4],s[4:6],s[6:8],
s[8:10],s[10:12],s[12:14])))
try: return apply(Timestamp, tuple(parts))
except: return None
def DateTime_or_None(s):
try: return ISO.ParseDateTime(s)
except: return None
def TimeDelta_or_None(s):
try: return ISO.ParseTimeDelta(s)
except: return None
def Date_or_None(s):
try: return ISO.ParseDate(s)
except: return None
conversions[FIELD_TYPE.TIMESTAMP] = mysql_timestamp_converter
conversions[FIELD_TYPE.DATETIME] = DateTime_or_None
conversions[FIELD_TYPE.TIME] = TimeDelta_or_None
conversions[FIELD_TYPE.DATE] = Date_or_None
def DateTime2literal(d, c):
"""Format a DateTime object as an ISO timestamp."""
return escape(format_TIMESTAMP(d),c)
def DateTimeDelta2literal(d, c):
"""Format a DateTimeDelta object as a time."""
return escape(format_TIME(d),c)
conversions[DateTimeType] = DateTime2literal
conversions[DateTimeDeltaType] = DateTimeDelta2literal
except ImportError:
# no DateTime? We'll muddle through somehow.
def DateFromTicks(ticks):
"""Convert UNIX ticks to ISO date format."""
return strftime("%Y-%m-%d", localtime(ticks))
def TimeFromTicks(ticks):
"""Convert UNIX ticks to time format."""
return strftime("%H:%M:%S", localtime(ticks))
def TimestampFromTicks(ticks):
"""Convert UNIX ticks to ISO timestamp format."""
return strftime("%Y-%m-%d %H:%M:%S", localtime(ticks))
def format_DATE(d):
"""Format a date as a date (does nothing, you don't have mx.DateTime)."""
return d
format_TIME = format_TIMESTAMP = format_DATE
__all__ = [ 'conversions', 'DateFromTicks', 'TimeFromTicks',
'TimestampFromTicks', 'format_DATE', 'format_TIME',
'format_TIMESTAMP' ]

View File

@@ -24,8 +24,6 @@ class BaseCursor:
self.rowcount = -1
self.arraysize = 100
self._executed = None
self._transaction = 0
self.__c_locked = 0
def __del__(self):
self.close()
@@ -33,10 +31,7 @@ class BaseCursor:
def close(self):
"""Close the cursor. No further queries will be possible."""
if not self.__conn: return
self._end()
self.__conn = None
self._executed = None
self._transaction = None
def _check_executed(self):
if not self._executed:
@@ -58,12 +53,12 @@ class BaseCursor:
"""Execute a query.
query -- string, query to execute on server
args -- sequence or mapping, parameters to use with query.
args -- optional sequence or mapping, parameters to use with query.
returns long integer rows affected, if any"""
from types import ListType, TupleType
qc = self._get_db().converter
if not args:
if args is None:
r = self._query(query)
elif type(args) is ListType and type(args[0]) is TupleType:
r = self.executemany(query, args) # deprecated
@@ -105,7 +100,7 @@ class BaseCursor:
except TypeError, msg:
if msg.args[0] in ("not enough arguments for format string",
"not all arguments converted"):
raise ProgrammingError, (0, msg.args[0])
raise ProgrammingError, msg.args[0]
else:
raise
r = self._query(join(q,',\n'))
@@ -113,20 +108,16 @@ class BaseCursor:
return r
def __do_query(self, q):
from string import split, atoi
db = self._get_db()
if self._transaction: self._begin()
try:
db.query(q)
self._result = self._get_result()
self.rowcount = db.affected_rows()
self.description = self._result and self._result.describe() or None
self._insert_id = db.insert_id()
self._info = db.info()
self._check_for_warnings()
except:
self._end()
raise
db.query(q)
self._result = self._get_result()
self.rowcount = db.affected_rows()
self.description = self._result and self._result.describe() or None
self._insert_id = db.insert_id()
self._info = db.info()
self._check_for_warnings()
return self.rowcount
def _check_for_warnings(self): pass
@@ -143,35 +134,9 @@ class BaseCursor:
self._check_executed()
return self._insert_id
def nextset(self):
"""Does nothing. Required by DB API."""
self._check_executed()
return None
def _fetch_row(self, size=1):
return self._result.fetch_row(size, self._fetch_type)
def _begin(self):
self.__conn._begin()
self._transaction = 1
def _end(self):
self._transaction = 0
self.__conn._end()
def _acquire(self):
if self.__c_locked: return
self.__conn._acquire()
self.__c_locked = 1
def _release(self):
if not self.__conn: return
self.__conn._release()
self.__c_locked = 0
def _is_transactional(self):
return self.__conn._transactional
class CursorWarningMixIn:
@@ -194,10 +159,6 @@ class CursorStoreResultMixIn:
result set can be very large, consider adding a LIMIT clause to your
query, or using CursorUseResultMixIn instead."""
def __init__(self, connection):
BaseCursor.__init__(self, connection)
self._acquire()
def _get_result(self): return self._get_db().store_result()
def close(self):
@@ -206,16 +167,11 @@ class CursorStoreResultMixIn:
BaseCursor.close(self)
def _query(self, q):
self._acquire()
try:
rowcount = self._BaseCursor__do_query(q)
self._rows = self._result and self._fetch_row(0) or ()
self._pos = 0
del self._result
if not self._is_transactional: self._end()
return rowcount
finally:
self._release()
rowcount = self._BaseCursor__do_query(q)
self._rows = self._result and self._fetch_row(0) or ()
self._pos = 0
del self._result
return rowcount
def fetchone(self):
"""Fetches a single row from the cursor."""
@@ -269,7 +225,6 @@ class CursorUseResultMixIn:
def close(self):
"""Close the cursor. No further queries can be executed."""
self._release()
self._result = None
BaseCursor.close(self)
@@ -279,19 +234,22 @@ class CursorUseResultMixIn:
"""Fetches a single row from the cursor."""
self._check_executed()
r = self._fetch_row(1)
return r and r[0] or None
if r: return r[0]
return None
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."""
self._check_executed()
return self._fetch_row(size or self.arraysize)
r = self._fetch_row(size or self.arraysize)
return r
def fetchall(self):
"""Fetchs all available rows from the cursor."""
self._check_open()
return self._fetch_row(0)
self._check_executed()
r = self._fetch_row(0)
return r
class CursorTupleRowsMixIn:

186
MySQLdb/MySQLdb/data.py Normal file
View File

@@ -0,0 +1,186 @@
"""data module
This module provides some useful classes for dealing with MySQL data.
"""
from time import strftime, localtime
from _mysql import string_literal
class Set:
"""A simple class for handling sets. Sets are immutable in the same
way numbers are."""
def __init__(self, *values):
"""Use values to initialize the Set."""
self._values = values
def contains(self, value):
"""Returns true if the value is contained within the Set."""
return value in self._values
def __str__(self):
"""Returns the values as a comma-separated string."""
from string import join
return join(map(str, self._values),',')
def __repr__(self):
return "Set%s" % `self._values`
def __add__(self, other):
"""Union."""
if isinstance(other, Set):
for v in other._values:
self = self + v
elif other not in self._values:
self = apply(Set, self._values+(other,))
return self
def __sub__(self, other):
if isinstance(other, Set):
for v in other._values:
if v in self:
self = self - v
elif other in self:
values = list(self._values)
values.remove(other)
return apply(Set, tuple(values))
return self
def __mul__(self, other):
"Intersection."
intersection = Set()
if isinstance(other, Set):
union = self + other
intersection = union
for v in union._values:
if v not in self or v not in other:
intersection = intersection - v
elif other in self:
intersection = apply(Set, (other,))
return intersection
def __mod__(self, other):
"Disjoint."
return (self+other)-(self*other)
def __getitem__(self, n):
return self._values[n]
def __len__(self):
return len(self._values)
def __hash__(self):
return hash(self._values)
def __cmp__(self, other):
if isinstance(other, Set):
d = self % other
if d._values:
return 1
else:
return 0
if other in self._values:
return 0
return -1
try:
try:
# new packaging
from mx.DateTime import Date, Time, Timestamp, ISO, \
DateTimeType, DateTimeDeltaType
except ImportError:
# old packaging, deprecated
from DateTime import Date, Time, Timestamp, ISO, \
DateTimeType, DateTimeDeltaType
def DateFromTicks(ticks):
"""Convert UNIX ticks into a mx.DateTime.Date."""
return apply(Date, localtime(ticks)[:3])
def TimeFromTicks(ticks):
"""Convert UNIX ticks into a mx.DateTime.Time."""
return apply(Time, localtime(ticks)[3:6])
def TimestampFromTicks(ticks):
"""Convert UNIX ticks into a mx.DateTime.Timestamp."""
return apply(Timestamp, localtime(ticks)[:6])
def format_DATE(d):
"""Format a DateTime object as an ISO date."""
return d.strftime("%Y-%m-%d")
def format_TIME(d):
"""Format a DateTime object as a time value."""
return d.strftime("%H:%M:%S")
def format_TIMESTAMP(d):
"""Format a DateTime object as an ISO timestamp."""
return d.strftime("%Y-%m-%d %H:%M:%S")
def DateTime_or_None(s):
try: return ISO.ParseDateTime(s)
except: return None
def TimeDelta_or_None(s):
try: return ISO.ParseTimeDelta(s)
except: return None
def Date_or_None(s):
try: return ISO.ParseDate(s)
except: return None
except ImportError:
# no DateTime? We'll muddle through somehow.
DateTimeDeltaType = "DateTimeDeltaType"
DateTimeType = "DateTimeType"
def Date(year, month, day):
"""Construct an ISO date string."""
return "%04d-%02d-%02d" % (year, month, day)
def Time(hour, min, sec):
"""Construct a TIME string."""
return "%02d:%02d:%02d" % (hour, min, sec)
def Timestamp(year, month, day, hour, min, sec):
"""Construct an ISO timestamp."""
return "%04d-%02d-%02d %02d:%02d:%02d" % \
(year, month, day, hour, min, sec)
def DateFromTicks(ticks):
"""Convert UNIX ticks to ISO date format."""
return strftime("%Y-%m-%d", localtime(ticks))
def TimeFromTicks(ticks):
"""Convert UNIX ticks to time format."""
return strftime("%H:%M:%S", localtime(ticks))
def TimestampFromTicks(ticks):
"""Convert UNIX ticks to ISO timestamp format."""
return strftime("%Y-%m-%d %H:%M:%S", localtime(ticks))
def format_DATE(d):
"""Format a date as a date (does nothing, you don't have mx.DateTime)."""
return d
format_TIME = format_TIMESTAMP = format_DATE = \
TimeDelta_or_None = Date_or_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_TIME(d),c)
def mysql_timestamp_converter(s):
"""Convert a MySQL TIMESTAMP to a Timestamp object."""
s = s + "0"*(14-len(s)) # padding
parts = map(int, filter(None, (s[:4],s[4:6],s[6:8],
s[8:10],s[10:12],s[12:14])))
try: return apply(Timestamp, tuple(parts))
except: return None

View File

@@ -1 +1 @@
static char _mysql__version__[] = "0.9.0b1";
static char _mysql__version__[] = "0.9.0b2";

View File

@@ -15,8 +15,7 @@ others. However, the older version is a) not thread-friendly (database
operations could cause all other threads to block), b) written for
MySQL 3.21 (does not compile against newer versions without patches),
c) apparently not actively maintained. MySQLdb is a completely new
module, distributed free of charge under a license derived from the
Python license.
module, distributed free of charge under the GNU Public License.
<p>
<sect1>Platforms
@@ -70,14 +69,14 @@ InnoDB. GEMINI tables are slated for MySQL-4.0. Note that MySQL
generally operates in <tt/AUTOCOMMIT/ mode by default, and MySQLdb
assumes that <tt/AUTOCOMMIT/ is on by default. To change this, use the
<tt/autocommit(n)/ method; otherwise MySQLdb will not know that that
you have changed it, and this affects locking for transactions.
you have changed it.
<sect1>DateTime <p>If you have the <htmlurl
url="http://www.lemburg.com/files/python/mxDateTime.html"
name="mx.DateTime"> package installed (recommended), MySQLdb will use
it for date-related objects. Otherwise, these will be returned to
Python as strings. You can also modify the type conversion
dictionaries to return these as other object classes, if you prefer.
dictionary to return these as other object classes, if you prefer.
<sect1>MySQLmodule<label id="MySQLmodule">
<p>
@@ -389,7 +388,7 @@ MySQLdb.
location of UNIX socket. Default: use TCP.
<tag>conv</tag> type conversion dictionary.
Default: a copy of <tt/MySQLdb.converters.conv/
Default: a copy of <tt/MySQLdb.converters.conversions/
<tag>compress</tag> Enable protocol compression. Default: no compression.
@@ -411,36 +410,36 @@ MySQLdb.
unless overridden. Default: <tt/MySQLdb.cursors.Cursor/.
<em/This must be a keyword parameter./
<tag>threads</tag> boolean, to enable use of threading within
<tt/MySQLdb/. Default: 1 (enabled). Set this to 0 if you
don't want to or can't use threads.
<em/This must be a keyword parameter./
</descrip>
<tag>apilevel</tag>
String constant stating the supported DB API level. '2.0'
<tag>threadsafety</tag> Integer constant stating the level of thread
safety the interface supports. As of MySQLdb version 0.2.0, this
is set to 2, which means: Threads may share the module and
connections. Cursors employ a lock in the connection object to
ensure that cursors do not use the connection at the same
time. Generally, sharing a connection probably reduces
performance; the MySQL server maintains a seperate thread for
each connection. It is recommended that connections not be
shared between threads. See the MySQL documentation for more
details. As of 0.9.0, there is an additional blocking lock that
is used if transactioning is available, to insure a connection
cannot be shared between two threads if a transaction is in
progress. Note that two cursors in the same thread cannot
attempt to acquire the connection lock at the same time; this
would produce deadlock, and <tt/ProgrammingError/ is
raised. However, multiple cursors in the same thread can acquire
the transaction lock. <tt/commit()/ and <tt/rollback()/ release
the transaction lock. The general rule here is, cursors acquire
locks, commit and rollback release them. Fortunately, you don't
need to think about this too much.
safety the interface supports. As of MySQLdb version 0.9.0, this
is set to 1, which means: Threads may share the module.
<p>The MySQL protocol can not handle multiple threads using the
same connection at once. Some earlier versions of MySQLdb utilized locking
to achieve a threadsafety of 2. While this is not terribly hard
to accomplish using the standard Cursor class (which uses
<tt/mysql_store_result()/), it is complicated by SSCursor (which
uses <tt/mysql_use_result()/; with the latter you must ensure
all the rows have been read before another query can be executed.
It is further complicated by the addition of transactions, since
transactions start when a cursor execute a query, but end when
<tt/COMMIT/ or <tt/ROLLBACK/ is executed by the Connection object.
Two threads cannot share a connection while a transaction is in
progress, in addition to not being able to share it during query
execution. This excessively complicated the code to the point where
it just isn't worth it. <p>The general upshot of this is: Don't
share connections between threads. It's really not worth your effort
or mine, and in the end, will probably hurt performance, since the MySQL
server runs a separate thread for each connection.
You can certainly do things like
cache connections in a pool, and give those connections to one
thread at a time. If you let two threads use a connection simultaneously,
the MySQL client library will probably upchuck and die.
You have been warned.
<tag>paramstyle</tag> String constant stating the type of parameter
marker formatting expected by the interface. Set to 'format' =
@@ -459,7 +458,7 @@ MySQLdb.
regardless of type. The interface performs all necessary
quoting.
<tag><label id="type_conv">conv</tag> A dictionary mapping MySQL types
<tag><label id="conversions">conv</tag> A dictionary mapping MySQL types
(from <TT>FIELD_TYPE.*</TT>) to callable Python objects (usually
functions) which convert from a string to the desired type; and
mapping Python types to callable Python objects which convert
@@ -467,10 +466,10 @@ MySQLdb.
initialized with reasonable defaults for most types. When
creating a Connection object, you can pass your own type
converter dictionary as a keyword parameter. Otherwise, it uses
a copy of <tt>MySQLdb.converters.conv</tt>. The dictionary
a copy of <tt>MySQLdb.converters.conversions</tt>. The dictionary
includes some of the factory functions from the
<tt>DateTime</tt> module, if it is available. Several
non-standard types (<tt>SET, ENUM</tt>) are returned as strings,
non-standard types are returned as strings,
which is how MySQL returns all columns. For more details, see
the built-in module documentation.
@@ -545,9 +544,6 @@ Compatibility note: The older <ref id="MySQLmodule"> defines this method,
cursor class without warnings, then you might want to use
this. See the MySQL docs for <tt/mysql_info()/. (Non-standard)
<tag>nextset()</tag>
Not implemented.
<tag>setinputsizes()</tag>
Does nothing, successfully.
@@ -674,12 +670,7 @@ entire result set is stored on the client side.
<tag><label id="SSCursor">CursorUseResultMixIn</tag> Causes the cursor to use the
<tt>mysql_use_result()</tt> function to get the query result. The
result set is stored on the server side and is transferred row by row
using fetch operations. Not recommended, particularly for threaded
applications that share connections. Note that creating the cursor
causes it to acquire a lock on the connection object, and this is
not released until the cursor is deleted or <tt/cursor.close()/.
If you aren't careful about this, it can result in deadlock, which
is bad.
using fetch operations.
<tag>CursorTupleRowsMixIn</tag> Causes the cursor to return rows
as a tuple of the column values.
@@ -702,8 +693,7 @@ and <tt>BaseCursor</tt>, i.e. it raises <tt>Warning</tt>, uses
dictionaries.
<tag>SSCursor</tag> A "server-side" cursor. Like <tt/Cursor/ but uses
<tt/CursorUseResultMixIn/. Thread-safe, but not recommended for
threaded applications which share connections.
<tt/CursorUseResultMixIn/.
Use only if you are dealing with potentially large result sets.
<tag/SSDictCursor/ Like <tt/SSCursor/ except it returns rows as

View File

@@ -75,7 +75,7 @@ MySQLdb. MySQLdb is free software.
setup (# Distribution meta-data
name = "MySQL-python",
version = "0.9.0b1",
version = "0.9.0b2",
description = "An interface to MySQL",
long_description=long_description,
author = "Andy Dustman",
@@ -89,6 +89,7 @@ setup (# Distribution meta-data
"MySQLdb.converters",
"MySQLdb.connections",
"MySQLdb.cursors",
"MySQLdb.data",
"MySQLdb.constants.CR",
"MySQLdb.constants.FIELD_TYPE",
"MySQLdb.constants.ER",