Author: ianb
Date: 2004-03-30 21:55:04 -0500 (Tue, 30 Mar 2004)
New Revision: 84
Modified:
trunk/SQLObject/sqlobject/dbconnection.py
trunk/SQLObject/sqlobject/dbm/dbmconnection.py
trunk/SQLObject/sqlobject/firebird/firebirdconnection.py
trunk/SQLObject/sqlobject/mysql/mysqlconnection.py
trunk/SQLObject/sqlobject/postgres/pgconnection.py
trunk/SQLObject/sqlobject/sqlite/sqliteconnection.py
trunk/SQLObject/sqlobject/sybase/sybaseconnection.py
Log:
* Fixed threading issue with Iterator, to match fix in 0.5
(connections were being released multiple times)
* Expanded URIs to allow keyword (GET) arguments
* Put in (optionally) more thorough debugging output
Modified: trunk/SQLObject/sqlobject/dbconnection.py
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/SQLObject/sqlobject/dbconnection.py 2004-03-31 02:48:02 UTC (re=
v 83)
+++ trunk/SQLObject/sqlobject/dbconnection.py 2004-03-31 02:55:04 UTC (re=
v 84)
@@ -13,6 +13,7 @@
import col
from joins import sorter
from converters import sqlrepr
+import urllib
=20
warnings.filterwarnings("ignore", "DB-API extension cursor.lastrowid use=
d")
=20
@@ -21,10 +22,12 @@
class DBConnection:
=20
def __init__(self, name=3DNone, debug=3DFalse, debugOutput=3DFalse,
- cache=3DTrue, style=3DNone, autoCommit=3DTrue):
+ cache=3DTrue, style=3DNone, autoCommit=3DTrue,
+ debugThreading=3DFalse):
self.name =3D name
self.debug =3D debug
self.debugOutput =3D debugOutput
+ self.debugThreading =3D debugThreading
self.cache =3D CacheSet(cache=3Dcache)
self.doCache =3D cache
self.style =3D style
@@ -81,7 +84,15 @@
else:
user =3D password =3D None
path =3D '/' + rest
- return user, password, host, path
+ args =3D {}
+ if path.find('?') !=3D -1:
+ path, arglist =3D path.split('?', 1)
+ arglist =3D arglist.split('&')
+ for single in arglist:
+ argname, argvalue =3D single.split('=3D', 1)
+ argvalue =3D urllib.unquote(argvalue)
+ args[argname] =3D argvalue
+ return user, password, host, path, args
_parseURI =3D staticmethod(_parseURI)
=20
class DBAPI(DBConnection):
@@ -117,11 +128,26 @@
self._connectionNumbers[id(newConn)] =3D self._connectio=
nCount
self._connectionCount +=3D 1
val =3D self._pool.pop()
+ if self.debug:
+ s =3D 'ACQUIRE'
+ if self._pool is not None:
+ s +=3D ' pool=3D[%s]' % ', '.join([str(self._connect=
ionNumbers[id(v)]) for v in self._pool])
+ self.printDebug(val, s, 'Pool')
return val
finally:
self._poolLock.release()
=20
def releaseConnection(self, conn, explicit=3DFalse):
+ if self.debug:
+ if explicit:
+ s =3D 'RELEASE (explicit)'
+ else:
+ s =3D 'RELEASE (implicit, autocommit=3D%s)' % self.autoC=
ommit
+ if self._pool is None:
+ s +=3D ' no pooling'
+ else:
+ s +=3D ' pool=3D[%s]' % ', '.join([str(self._connectionN=
umbers[id(v)]) for v in self._pool])
+ self.printDebug(conn, s, 'Pool')
if self.supportTransactions:
if self.autoCommit =3D=3D 'exception':
if self.debug:
@@ -137,7 +163,11 @@
self.printDebug(conn, 'auto', 'ROLLBACK')
conn.rollback()
if self._pool is not None:
- self._pool.append(conn)
+ if conn not in self._pool:
+ # @@: We can get duplicate releasing of connections with
+ # the __del__ in Iteration (unfortunately, not sure why
+ # it happens)
+ self._pool.append(conn)
=20
def printDebug(self, conn, s, name, type=3D'query'):
if type =3D=3D 'query':
@@ -147,12 +177,20 @@
s =3D repr(s)
n =3D self._connectionNumbers[id(conn)]
spaces =3D ' '*(8-len(name))
- print '%(n)2i/%(name)s%(spaces)s%(sep)s %(s)s' % locals()
+ if self.debugThreading:
+ threadName =3D threading.currentThread().getName()
+ threadName =3D (':' + threadName + ' '*(8-len(threadName)))
+ else:
+ threadName =3D ''
+ print '%(n)2i%(threadName)s/%(name)s%(spaces)s%(sep)s %(s)s' % l=
ocals()
=20
+ def _executeRetry(self, conn, cursor, query):
+ return cursor.execute(query)
+
def _query(self, conn, s):
if self.debug:
self.printDebug(conn, s, 'Query')
- conn.cursor().execute(s)
+ self._executeRetry(conn, conn.cursor(), s)
=20
def query(self, s):
return self._runWithConnection(self._query, s)
@@ -161,7 +199,7 @@
if self.debug:
self.printDebug(conn, s, 'QueryAll')
c =3D conn.cursor()
- c.execute(s)
+ self._executeRetry(conn, c, s)
value =3D c.fetchall()
if self.debugOutput:
self.printDebug(conn, value, 'QueryAll', 'result')
@@ -174,7 +212,7 @@
if self.debug:
self.printDebug(conn, s, 'QueryOne')
c =3D conn.cursor()
- c.execute(s)
+ self._executeRetry(conn, c, s)
value =3D c.fetchone()
if self.debugOutput:
self.printDebug(conn, value, 'QueryOne', 'result')
@@ -390,13 +428,12 @@
self.query =3D self.dbconn.queryForSelect(select)
if dbconn.debug:
dbconn.printDebug(rawconn, self.query, 'Select')
- self.cursor.execute(self.query)
+ self.dbconn._executeRetry(self.rawconn, self.cursor, self.query)
=20
def next(self):
result =3D self.cursor.fetchone()
if result is None:
- if not self.keepConnection:
- self.dbconn.releaseConnection(self.rawconn)
+ self._cleanup()
raise StopIteration
if self.select.ops.get('lazyColumns', 0):
obj =3D self.select.sourceClass.get(result[0], connection=3D=
self.dbconn)
@@ -405,11 +442,20 @@
obj =3D self.select.sourceClass.get(result[0], selectResults=
=3Dresult[1:], connection=3Dself.dbconn)
return obj
=20
- def __del__(self):
+ def _cleanup(self):
+ if self.query is None:
+ # already cleaned up
+ return
+ self.query =3D None
if not self.keepConnection:
self.dbconn.releaseConnection(self.rawconn)
+ self.dbconn =3D self.rawconn =3D self.select =3D self.cursor =3D=
None
=20
+ def __del__(self):
+ self._cleanup()
+ =20
=20
+
class Transaction(object):
=20
def __init__(self, dbConnection):
@@ -441,8 +487,13 @@
=20
def iterSelect(self, select):
self.assertActive()
- return Iteration(self, self._connection,
- select, keepConnection=3DTrue)
+ # We can't keep the cursor open with results in a transaction,
+ # because we might want to use the connection while we're
+ # still iterating through the results.
+ # @@: But would it be okay for psycopg, with threadsafety
+ # level 2?
+ return list(Iteration(self, self._connection,
+ select, keepConnection=3DTrue))
=20
def commit(self):
if self._obsolete:
Modified: trunk/SQLObject/sqlobject/dbm/dbmconnection.py
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/SQLObject/sqlobject/dbm/dbmconnection.py 2004-03-31 02:48:02 UT=
C (rev 83)
+++ trunk/SQLObject/sqlobject/dbm/dbmconnection.py 2004-03-31 02:55:04 UT=
C (rev 84)
@@ -127,12 +127,12 @@
FileConnection.__init__(self, **kw)
=20
def connectionFromURI(cls, uri):
- user, password, host, path =3D self._parseURI(uri, expectHost=3D=
False)
+ user, password, host, path, args =3D self._parseURI(uri, expectH=
ost=3DFalse)
assert host is None
assert user is None and password is None, \
- "SQLite cannot accept usernames or passwords"
+ "DBM cannot accept usernames or passwords"
path =3D '/' + path
- return cls(path)
+ return cls(path, **args)
connectionFromURI =3D classmethod(connectionFromURI)
=20
def _newID(self, table):
Modified: trunk/SQLObject/sqlobject/firebird/firebirdconnection.py
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/SQLObject/sqlobject/firebird/firebirdconnection.py 2004-03-31 0=
2:48:02 UTC (rev 83)
+++ trunk/SQLObject/sqlobject/firebird/firebirdconnection.py 2004-03-31 0=
2:55:04 UTC (rev 84)
@@ -28,12 +28,12 @@
DBAPI.__init__(self, **kw)
=20
def connectionFromURI(cls, uri):
- auth, password, host, path =3D cls._parseURI(uri)
+ auth, password, host, path, args =3D cls._parseURI(uri)
if not password:
password =3D 'masterkey'
if not auth:
auth=3D'sysdba'
- return cls(host, db=3Dpath, user=3Dauth, passwd=3Dpassword)
+ return cls(host, db=3Dpath, user=3Dauth, passwd=3Dpassword, **ar=
gs)
connectionFromURI =3D classmethod(connectionFromURI)
=20
def _runWithConnection(self, meth, *args):
Modified: trunk/SQLObject/sqlobject/mysql/mysqlconnection.py
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/SQLObject/sqlobject/mysql/mysqlconnection.py 2004-03-31 02:48:0=
2 UTC (rev 83)
+++ trunk/SQLObject/sqlobject/mysql/mysqlconnection.py 2004-03-31 02:55:0=
4 UTC (rev 84)
@@ -19,15 +19,27 @@
DBAPI.__init__(self, **kw)
=20
def connectionFromURI(cls, uri):
- user, password, host, path =3D cls._parseURI(uri)
+ user, password, host, path, args =3D cls._parseURI(uri)
return cls(db=3Dpath.strip('/'), user=3Duser or '', passwd=3Dpas=
sword or '',
- host=3Dhost or 'localhost')
+ host=3Dhost or 'localhost', **args)
connectionFromURI =3D classmethod(connectionFromURI)
=20
def makeConnection(self):
return MySQLdb.connect(host=3Dself.host, db=3Dself.db,
user=3Dself.user, passwd=3Dself.passwd)
=20
+ def _executeRetry(self, conn, cursor, query):
+ while 1:
+ try:
+ return cursor.execute(query)
+ except MySQLdb.OperationalError, e:
+ if e.args[0] =3D=3D 2013:
+ # This is a=20
+ if self.debug:
+ self.printDebug(conn, str(e), 'ERROR')
+ else:
+ raise
+
def _queryInsertID(self, conn, table, idName, id, names, values):
c =3D conn.cursor()
if id is not None:
@@ -36,7 +48,7 @@
q =3D self._insertSQL(table, names, values)
if self.debug:
self.printDebug(conn, q, 'QueryIns')
- c.execute(q)
+ self._executeRetry(conn, c, q)
if id is None:
id =3D c.insert_id()
if self.debugOutput:
Modified: trunk/SQLObject/sqlobject/postgres/pgconnection.py
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/SQLObject/sqlobject/postgres/pgconnection.py 2004-03-31 02:48:0=
2 UTC (rev 83)
+++ trunk/SQLObject/sqlobject/postgres/pgconnection.py 2004-03-31 02:55:0=
4 UTC (rev 84)
@@ -42,16 +42,15 @@
DBAPI.__init__(self, **kw)
=20
def connectionFromURI(cls, uri):
- user, password, host, path =3D cls._parseURI(uri)
+ user, password, host, path, args =3D cls._parseURI(uri)
path =3D path.strip('/')
- return cls(host=3Dhost, db=3Dpath, user=3Duser, passwd=3Dpasswor=
d)
+ return cls(host=3Dhost, db=3Dpath, user=3Duser, passwd=3Dpasswor=
d, **args)
connectionFromURI =3D classmethod(connectionFromURI)
=20
def _setAutoCommit(self, conn, auto):
conn.autocommit(auto)
=20
def makeConnection(self):
- print 'DSN', self.dsn
conn =3D self.pgmodule.connect(self.dsn)
if self.autoCommit:
conn.autocommit(1)
Modified: trunk/SQLObject/sqlobject/sqlite/sqliteconnection.py
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/SQLObject/sqlobject/sqlite/sqliteconnection.py 2004-03-31 02:48=
:02 UTC (rev 83)
+++ trunk/SQLObject/sqlobject/sqlite/sqliteconnection.py 2004-03-31 02:55=
:04 UTC (rev 84)
@@ -21,10 +21,10 @@
DBAPI.__init__(self, **kw)
=20
def connectionFromURI(cls, uri):
- user, password, host, path =3D cls._parseURI(uri)
+ user, password, host, path, args =3D cls._parseURI(uri)
assert host is None, "SQLite can only be used locally (with a UR=
I like sqlite:///file or sql:/file, not %r)" % uri
assert user is None and password is None, "You may not provide u=
sernames or passwords for SQLite databases"
- return cls(filename=3D'/' + path)
+ return cls(filename=3D'/' + path, **args)
connectionFromURI =3D classmethod(connectionFromURI)
=20
def _setAutoCommit(self, conn, auto):
Modified: trunk/SQLObject/sqlobject/sybase/sybaseconnection.py
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/SQLObject/sqlobject/sybase/sybaseconnection.py 2004-03-31 02:48=
:02 UTC (rev 83)
+++ trunk/SQLObject/sqlobject/sybase/sybaseconnection.py 2004-03-31 02:55=
:04 UTC (rev 84)
@@ -26,9 +26,9 @@
DBAPI.__init__(self, **kw)
=20
def connectionFromURI(cls, uri):
- user, password, host, path =3D cls._parseURI(uri)
+ user, password, host, path, args =3D cls._parseURI(uri)
return cls(user=3Duser, passwd=3Dpassword, host=3Dhost or 'local=
host',
- db=3Dpath)
+ db=3Dpath, **args)
connectionFromURI =3D classmethod(connectionFromURI)
=20
def insert_id(self, conn):
|