mirror of
https://github.com/PyMySQL/mysqlclient.git
synced 2026-03-13 08:00:02 +08:00
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:
@@ -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']
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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' ]
|
||||
|
||||
@@ -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
186
MySQLdb/MySQLdb/data.py
Normal 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
|
||||
@@ -1 +1 @@
|
||||
static char _mysql__version__[] = "0.9.0b1";
|
||||
static char _mysql__version__[] = "0.9.0b2";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user