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, 9,
0, 0,
"beta", "beta",
1) 2)
if version_info[3] == "final": __version__ = "%d.%d.%d" % version_info[:3] if version_info[3] == "final": __version__ = "%d.%d.%d" % version_info[:3]
else: __version__ = "%d.%d.%d%1.1s%d" % version_info[:5] else: __version__ = "%d.%d.%d%1.1s%d" % version_info[:5]
@ -31,46 +31,28 @@ if __version__ != getattr(_mysql, '__version__', None):
(__version__, _mysql.__version__) (__version__, _mysql.__version__)
threadsafety = 2 threadsafety = 1
apilevel = "2.0" apilevel = "2.0"
paramstyle = "format" 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 _mysql import *
from connections import Connection 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): def Connect(*args, **kwargs):
"""Factory function for connections.Connection.""" """Factory function for connections.Connection."""
@ -78,5 +60,17 @@ def Connect(*args, **kwargs):
connect = Connect 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() read_default_group -- see the MySQL documentation for mysql_options()
cursorclass -- class object, used to create cursors or cursors.Cursor. cursorclass -- class object, used to create cursors or cursors.Cursor.
This parameter MUST be specified as a keyword parameter. 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. Returns a Connection object.
@ -50,9 +48,6 @@ class Connection:
from converters import conversions from converters import conversions
import types import types
kwargs2 = kwargs.copy() kwargs2 = kwargs.copy()
self.__threads = kwargs.get('threads',1)
if kwargs.has_key('threads'):
del kwargs2['threads']
if not kwargs.has_key('conv'): if not kwargs.has_key('conv'):
kwargs2['conv'] = conversions.copy() kwargs2['conv'] = conversions.copy()
if kwargs.has_key('cursorclass'): if kwargs.has_key('cursorclass'):
@ -64,69 +59,9 @@ class Connection:
self._db.converter[types.StringType] = self._db.string_literal self._db.converter[types.StringType] = self._db.string_literal
self._transactional = self._db.server_capabilities & CLIENT.TRANSACTIONS self._transactional = self._db.server_capabilities & CLIENT.TRANSACTIONS
self._autocommit = 1 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): def __del__(self):
self.close() if hasattr(self, '_db'): 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()
def close(self): def close(self):
"""Close the connection. No further activity possible.""" """Close the connection. No further activity possible."""
@ -149,7 +84,6 @@ class Connection:
if self._transactional: if self._transactional:
self._db.query("COMMIT") self._db.query("COMMIT")
finally: finally:
self._end()
self._transactional = not self._autocommit self._transactional = not self._autocommit
def rollback(self): def rollback(self):
@ -160,7 +94,6 @@ class Connection:
else: else:
raise NotSupportedError, "Not supported by server" raise NotSupportedError, "Not supported by server"
finally: finally:
self._end()
self._transactional = not self._autocommit self._transactional = not self._autocommit
def cursor(self, cursorclass=None): 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 _mysql import string_literal, escape_sequence, escape_dict, escape, NULL
from constants import FIELD_TYPE from constants import FIELD_TYPE
from time import localtime, strftime from data import *
from string import split
import types import types
def Str2Set(s):
values = split(s, ',')
return apply(Set, tuple(values))
def Thing2Str(s, d): def Thing2Str(s, d):
"""Convert something into a string via str().""" """Convert something into a string via str()."""
return str(s) 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 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.""" first class it can find for which o is an instance."""
if d.has_key(o.__class__): return if d.has_key(o.__class__): return d[o.__class__](o, d)
d[o.__class__](o, d)
cl = filter(lambda x,o=o: cl = filter(lambda x,o=o:
type(x)==types.ClassType and isinstance(o,x), d.keys()) type(x)==types.ClassType and isinstance(o,x), d.keys())
if not cl: if not cl:
@ -82,6 +87,8 @@ conversions = {
types.DictType: escape_dict, types.DictType: escape_dict,
types.InstanceType: Instance2Str, types.InstanceType: Instance2Str,
types.StringType: Thing2Literal, # default types.StringType: Thing2Literal, # default
DateTimeType: DateTime2literal,
DateTimeDeltaType: DateTimeDelta2literal,
FIELD_TYPE.TINY: int, FIELD_TYPE.TINY: int,
FIELD_TYPE.SHORT: int, FIELD_TYPE.SHORT: int,
FIELD_TYPE.LONG: long, FIELD_TYPE.LONG: long,
@ -89,99 +96,11 @@ conversions = {
FIELD_TYPE.DOUBLE: float, FIELD_TYPE.DOUBLE: float,
FIELD_TYPE.LONGLONG: long, FIELD_TYPE.LONGLONG: long,
FIELD_TYPE.INT24: int, 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.rowcount = -1
self.arraysize = 100 self.arraysize = 100
self._executed = None self._executed = None
self._transaction = 0
self.__c_locked = 0
def __del__(self): def __del__(self):
self.close() self.close()
@ -33,10 +31,7 @@ class BaseCursor:
def close(self): def close(self):
"""Close the cursor. No further queries will be possible.""" """Close the cursor. No further queries will be possible."""
if not self.__conn: return if not self.__conn: return
self._end()
self.__conn = None self.__conn = None
self._executed = None
self._transaction = None
def _check_executed(self): def _check_executed(self):
if not self._executed: if not self._executed:
@ -58,12 +53,12 @@ class BaseCursor:
"""Execute a query. """Execute a query.
query -- string, query to execute on server 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""" returns long integer rows affected, if any"""
from types import ListType, TupleType from types import ListType, TupleType
qc = self._get_db().converter qc = self._get_db().converter
if not args: if args is None:
r = self._query(query) r = self._query(query)
elif type(args) is ListType and type(args[0]) is TupleType: elif type(args) is ListType and type(args[0]) is TupleType:
r = self.executemany(query, args) # deprecated r = self.executemany(query, args) # deprecated
@ -105,7 +100,7 @@ class BaseCursor:
except TypeError, msg: except TypeError, msg:
if msg.args[0] in ("not enough arguments for format string", if msg.args[0] in ("not enough arguments for format string",
"not all arguments converted"): "not all arguments converted"):
raise ProgrammingError, (0, msg.args[0]) raise ProgrammingError, msg.args[0]
else: else:
raise raise
r = self._query(join(q,',\n')) r = self._query(join(q,',\n'))
@ -113,20 +108,16 @@ class BaseCursor:
return r return r
def __do_query(self, q): def __do_query(self, q):
from string import split, atoi from string import split, atoi
db = self._get_db() db = self._get_db()
if self._transaction: self._begin() db.query(q)
try: self._result = self._get_result()
db.query(q) self.rowcount = db.affected_rows()
self._result = self._get_result() self.description = self._result and self._result.describe() or None
self.rowcount = db.affected_rows() self._insert_id = db.insert_id()
self.description = self._result and self._result.describe() or None self._info = db.info()
self._insert_id = db.insert_id() self._check_for_warnings()
self._info = db.info()
self._check_for_warnings()
except:
self._end()
raise
return self.rowcount return self.rowcount
def _check_for_warnings(self): pass def _check_for_warnings(self): pass
@ -143,35 +134,9 @@ class BaseCursor:
self._check_executed() self._check_executed()
return self._insert_id return self._insert_id
def nextset(self):
"""Does nothing. Required by DB API."""
self._check_executed()
return None
def _fetch_row(self, size=1): def _fetch_row(self, size=1):
return self._result.fetch_row(size, self._fetch_type) 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: class CursorWarningMixIn:
@ -194,10 +159,6 @@ class CursorStoreResultMixIn:
result set can be very large, consider adding a LIMIT clause to your result set can be very large, consider adding a LIMIT clause to your
query, or using CursorUseResultMixIn instead.""" 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 _get_result(self): return self._get_db().store_result()
def close(self): def close(self):
@ -206,16 +167,11 @@ class CursorStoreResultMixIn:
BaseCursor.close(self) BaseCursor.close(self)
def _query(self, q): def _query(self, q):
self._acquire() rowcount = self._BaseCursor__do_query(q)
try: self._rows = self._result and self._fetch_row(0) or ()
rowcount = self._BaseCursor__do_query(q) self._pos = 0
self._rows = self._result and self._fetch_row(0) or () del self._result
self._pos = 0 return rowcount
del self._result
if not self._is_transactional: self._end()
return rowcount
finally:
self._release()
def fetchone(self): def fetchone(self):
"""Fetches a single row from the cursor.""" """Fetches a single row from the cursor."""
@ -269,7 +225,6 @@ class CursorUseResultMixIn:
def close(self): def close(self):
"""Close the cursor. No further queries can be executed.""" """Close the cursor. No further queries can be executed."""
self._release()
self._result = None self._result = None
BaseCursor.close(self) BaseCursor.close(self)
@ -279,19 +234,22 @@ class CursorUseResultMixIn:
"""Fetches a single row from the cursor.""" """Fetches a single row from the cursor."""
self._check_executed() self._check_executed()
r = self._fetch_row(1) r = self._fetch_row(1)
return r and r[0] or None if r: return r[0]
return None
def fetchmany(self, size=None): def fetchmany(self, size=None):
"""Fetch up to size rows from the cursor. Result set may be smaller """Fetch up to size rows from the cursor. Result set may be smaller
than size. If size is not defined, cursor.arraysize is used.""" than size. If size is not defined, cursor.arraysize is used."""
self._check_executed() self._check_executed()
return self._fetch_row(size or self.arraysize) r = self._fetch_row(size or self.arraysize)
return r
def fetchall(self): def fetchall(self):
"""Fetchs all available rows from the cursor.""" """Fetchs all available rows from the cursor."""
self._check_open() self._check_executed()
return self._fetch_row(0) r = self._fetch_row(0)
return r
class CursorTupleRowsMixIn: 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 operations could cause all other threads to block), b) written for
MySQL 3.21 (does not compile against newer versions without patches), MySQL 3.21 (does not compile against newer versions without patches),
c) apparently not actively maintained. MySQLdb is a completely new c) apparently not actively maintained. MySQLdb is a completely new
module, distributed free of charge under a license derived from the module, distributed free of charge under the GNU Public License.
Python license.
<p> <p>
<sect1>Platforms <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 generally operates in <tt/AUTOCOMMIT/ mode by default, and MySQLdb
assumes that <tt/AUTOCOMMIT/ is on by default. To change this, use the assumes that <tt/AUTOCOMMIT/ is on by default. To change this, use the
<tt/autocommit(n)/ method; otherwise MySQLdb will not know that that <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 <sect1>DateTime <p>If you have the <htmlurl
url="http://www.lemburg.com/files/python/mxDateTime.html" url="http://www.lemburg.com/files/python/mxDateTime.html"
name="mx.DateTime"> package installed (recommended), MySQLdb will use name="mx.DateTime"> package installed (recommended), MySQLdb will use
it for date-related objects. Otherwise, these will be returned to it for date-related objects. Otherwise, these will be returned to
Python as strings. You can also modify the type conversion 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"> <sect1>MySQLmodule<label id="MySQLmodule">
<p> <p>
@ -389,7 +388,7 @@ MySQLdb.
location of UNIX socket. Default: use TCP. location of UNIX socket. Default: use TCP.
<tag>conv</tag> type conversion dictionary. <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. <tag>compress</tag> Enable protocol compression. Default: no compression.
@ -411,36 +410,36 @@ MySQLdb.
unless overridden. Default: <tt/MySQLdb.cursors.Cursor/. unless overridden. Default: <tt/MySQLdb.cursors.Cursor/.
<em/This must be a keyword parameter./ <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> </descrip>
<tag>apilevel</tag> <tag>apilevel</tag>
String constant stating the supported DB API level. '2.0' String constant stating the supported DB API level. '2.0'
<tag>threadsafety</tag> Integer constant stating the level of thread <tag>threadsafety</tag> Integer constant stating the level of thread
safety the interface supports. As of MySQLdb version 0.2.0, this safety the interface supports. As of MySQLdb version 0.9.0, this
is set to 2, which means: Threads may share the module and is set to 1, which means: Threads may share the module.
connections. Cursors employ a lock in the connection object to <p>The MySQL protocol can not handle multiple threads using the
ensure that cursors do not use the connection at the same same connection at once. Some earlier versions of MySQLdb utilized locking
time. Generally, sharing a connection probably reduces to achieve a threadsafety of 2. While this is not terribly hard
performance; the MySQL server maintains a seperate thread for to accomplish using the standard Cursor class (which uses
each connection. It is recommended that connections not be <tt/mysql_store_result()/), it is complicated by SSCursor (which
shared between threads. See the MySQL documentation for more uses <tt/mysql_use_result()/; with the latter you must ensure
details. As of 0.9.0, there is an additional blocking lock that all the rows have been read before another query can be executed.
is used if transactioning is available, to insure a connection It is further complicated by the addition of transactions, since
cannot be shared between two threads if a transaction is in transactions start when a cursor execute a query, but end when
progress. Note that two cursors in the same thread cannot <tt/COMMIT/ or <tt/ROLLBACK/ is executed by the Connection object.
attempt to acquire the connection lock at the same time; this Two threads cannot share a connection while a transaction is in
would produce deadlock, and <tt/ProgrammingError/ is progress, in addition to not being able to share it during query
raised. However, multiple cursors in the same thread can acquire execution. This excessively complicated the code to the point where
the transaction lock. <tt/commit()/ and <tt/rollback()/ release it just isn't worth it. <p>The general upshot of this is: Don't
the transaction lock. The general rule here is, cursors acquire share connections between threads. It's really not worth your effort
locks, commit and rollback release them. Fortunately, you don't or mine, and in the end, will probably hurt performance, since the MySQL
need to think about this too much. 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 <tag>paramstyle</tag> String constant stating the type of parameter
marker formatting expected by the interface. Set to 'format' = marker formatting expected by the interface. Set to 'format' =
@ -459,7 +458,7 @@ MySQLdb.
regardless of type. The interface performs all necessary regardless of type. The interface performs all necessary
quoting. 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 (from <TT>FIELD_TYPE.*</TT>) to callable Python objects (usually
functions) which convert from a string to the desired type; and functions) which convert from a string to the desired type; and
mapping Python types to callable Python objects which convert mapping Python types to callable Python objects which convert
@ -467,10 +466,10 @@ MySQLdb.
initialized with reasonable defaults for most types. When initialized with reasonable defaults for most types. When
creating a Connection object, you can pass your own type creating a Connection object, you can pass your own type
converter dictionary as a keyword parameter. Otherwise, it uses 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 includes some of the factory functions from the
<tt>DateTime</tt> module, if it is available. Several <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 which is how MySQL returns all columns. For more details, see
the built-in module documentation. 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 cursor class without warnings, then you might want to use
this. See the MySQL docs for <tt/mysql_info()/. (Non-standard) this. See the MySQL docs for <tt/mysql_info()/. (Non-standard)
<tag>nextset()</tag>
Not implemented.
<tag>setinputsizes()</tag> <tag>setinputsizes()</tag>
Does nothing, successfully. 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 <tag><label id="SSCursor">CursorUseResultMixIn</tag> Causes the cursor to use the
<tt>mysql_use_result()</tt> function to get the query result. 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 result set is stored on the server side and is transferred row by row
using fetch operations. Not recommended, particularly for threaded using fetch operations.
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.
<tag>CursorTupleRowsMixIn</tag> Causes the cursor to return rows <tag>CursorTupleRowsMixIn</tag> Causes the cursor to return rows
as a tuple of the column values. as a tuple of the column values.
@ -702,8 +693,7 @@ and <tt>BaseCursor</tt>, i.e. it raises <tt>Warning</tt>, uses
dictionaries. dictionaries.
<tag>SSCursor</tag> A "server-side" cursor. Like <tt/Cursor/ but uses <tag>SSCursor</tag> A "server-side" cursor. Like <tt/Cursor/ but uses
<tt/CursorUseResultMixIn/. Thread-safe, but not recommended for <tt/CursorUseResultMixIn/.
threaded applications which share connections.
Use only if you are dealing with potentially large result sets. Use only if you are dealing with potentially large result sets.
<tag/SSDictCursor/ Like <tt/SSCursor/ except it returns rows as <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 setup (# Distribution meta-data
name = "MySQL-python", name = "MySQL-python",
version = "0.9.0b1", version = "0.9.0b2",
description = "An interface to MySQL", description = "An interface to MySQL",
long_description=long_description, long_description=long_description,
author = "Andy Dustman", author = "Andy Dustman",
@ -89,6 +89,7 @@ setup (# Distribution meta-data
"MySQLdb.converters", "MySQLdb.converters",
"MySQLdb.connections", "MySQLdb.connections",
"MySQLdb.cursors", "MySQLdb.cursors",
"MySQLdb.data",
"MySQLdb.constants.CR", "MySQLdb.constants.CR",
"MySQLdb.constants.FIELD_TYPE", "MySQLdb.constants.FIELD_TYPE",
"MySQLdb.constants.ER", "MySQLdb.constants.ER",