Update of /cvsroot/sqlobject/SQLObject/SQLObject
In directory sc8-pr-cvs1:/tmp/cvs-serv26904/SQLObject
Modified Files:
DBConnection.py SQLObject.py
Log Message:
Lots of transaction fixes.
Index: DBConnection.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/DBConnection.py,v
retrieving revision 1.43
retrieving revision 1.44
diff -C2 -d -r1.43 -r1.44
*** DBConnection.py 6 Sep 2003 02:37:42 -0000 1.43
--- DBConnection.py 7 Sep 2003 00:49:03 -0000 1.44
***************
*** 8,11 ****
--- 8,12 ----
import atexit
import os
+ import new
import SQLBuilder
from Cache import CacheSet
***************
*** 19,22 ****
--- 20,24 ----
MySQLdb = None
psycopg = None
+ pgdb = None
sqlite = None
kinterbasdb = None
***************
*** 31,36 ****
class DBConnection:
! def __init__(self, name=None, debug=False, cache=True,
! style=None):
if name:
assert not _connections.has_key(name), 'A database by the name %s has already been created: %s' % (name, _connections[name])
--- 33,38 ----
class DBConnection:
! def __init__(self, name=None, debug=False, debugOutput=False,
! cache=True, style=None, autoCommit=True):
if name:
assert not _connections.has_key(name), 'A database by the name %s has already been created: %s' % (name, _connections[name])
***************
*** 38,45 ****
self.name = name
self.debug = debug
self.cache = CacheSet(cache=cache)
self.doCache = cache
self.style = style
!
def connectionForName(name):
--- 40,50 ----
self.name = name
self.debug = debug
+ self.debugOutput = debugOutput
self.cache = CacheSet(cache=cache)
self.doCache = cache
self.style = style
! self._connectionNumbers = {}
! self._connectionCount = 1
! self.autoCommit = autoCommit
def connectionForName(name):
***************
*** 63,67 ****
conn = self.getConnection()
val = meth(conn, *args)
- conn.commit()
self.releaseConnection(conn)
return val
--- 68,71 ----
***************
*** 70,74 ****
self._poolLock.acquire()
if not self._pool:
! self._pool.append(self.makeConnection())
val = self._pool.pop()
self._poolLock.release()
--- 74,81 ----
self._poolLock.acquire()
if not self._pool:
! newConn = self.makeConnection()
! self._pool.append(newConn)
! self._connectionNumbers[id(newConn)] = self._connectionCount
! self._connectionCount += 1
val = self._pool.pop()
self._poolLock.release()
***************
*** 76,85 ****
def releaseConnection(self, conn):
if self._pool is not None:
self._pool.append(conn)
def _query(self, conn, s):
if self.debug:
! print 'Query : %s' % s
conn.cursor().execute(s)
--- 83,116 ----
def releaseConnection(self, conn):
+ if self.supportTransactions:
+ if self.autoCommit == 'exception':
+ if self.debug:
+ self.printDebug(conn, 'auto/exception', 'ROLLBACK')
+ conn.rollback()
+ raise Exception, 'Object used outside of a transaction; implicit COMMIT or ROLLBACK not allowed'
+ elif self.autoCommit:
+ if self.debug:
+ self.printDebug(conn, 'auto', 'COMMIT')
+ conn.commit()
+ else:
+ if self.debug:
+ self.printDebug(conn, 'auto', 'ROLLBACK')
+ conn.rollback()
if self._pool is not None:
self._pool.append(conn)
+ def printDebug(self, conn, s, name, type='query'):
+ if type == 'query':
+ sep = ': '
+ else:
+ sep = '->'
+ s = repr(s)
+ n = self._connectionNumbers[id(conn)]
+ spaces = ' '*(8-len(name))
+ print '%(n)2i/%(name)s%(spaces)s%(sep)s %(s)s' % locals()
+
def _query(self, conn, s):
if self.debug:
! self.printDebug(conn, s, 'Query')
conn.cursor().execute(s)
***************
*** 89,96 ****
def _queryAll(self, conn, s):
if self.debug:
! print 'QueryAll: %s' % s
c = conn.cursor()
c.execute(s)
! return c.fetchall()
def queryAll(self, s):
--- 120,130 ----
def _queryAll(self, conn, s):
if self.debug:
! self.printDebug(conn, s, 'QueryAll')
c = conn.cursor()
c.execute(s)
! value = c.fetchall()
! if self.debugOutput:
! self.printDebug(conn, value, 'QueryAll', 'result')
! return value
def queryAll(self, s):
***************
*** 99,106 ****
def _queryOne(self, conn, s):
if self.debug:
! print 'QueryOne: %s' % s
c = conn.cursor()
c.execute(s)
! return c.fetchone()
def queryOne(self, s):
--- 133,143 ----
def _queryOne(self, conn, s):
if self.debug:
! self.printDebug(conn, s, 'QueryOne')
c = conn.cursor()
c.execute(s)
! value = c.fetchone()
! if self.debugOutput:
! self.printDebug(conn, value, 'QueryOne', 'result')
! return value
def queryOne(self, s):
***************
*** 118,139 ****
return self._runWithConnection(self._queryInsertID, table, idName, names, values)
! def iterSelect(self, select):
! conn = self.getConnection()
cursor = conn.cursor()
query = self.queryForSelect(select)
if self.debug:
! print "Select: %s" % query
cursor.execute(query)
while 1:
result = cursor.fetchone()
if result is None:
! self.releaseConnection(conn)
break
if select.ops.get('lazyColumns', 0):
! yield select.sourceClass(result[0])
else:
! obj = select.sourceClass(result[0], selectResults=result[1:])
yield obj
def countSelect(self, select):
q = "SELECT COUNT(*) FROM %s WHERE %s" % \
--- 155,182 ----
return self._runWithConnection(self._queryInsertID, table, idName, names, values)
! def _iterSelect(self, conn, select, withConnection=None,
! keepConnection=False):
cursor = conn.cursor()
query = self.queryForSelect(select)
if self.debug:
! self.printDebug(conn, query, 'Select')
cursor.execute(query)
while 1:
result = cursor.fetchone()
if result is None:
! if not keepConnection:
! self.releaseConnection(conn)
break
if select.ops.get('lazyColumns', 0):
! obj = select.sourceClass(result[0], connection=withConnection)
! yield obj
else:
! obj = select.sourceClass(result[0], selectResults=result[1:], connection=withConnection)
yield obj
+ def iterSelect(self, select):
+ return self._runWithConnection(self._iterSelect, select, self,
+ False)
+
def countSelect(self, select):
q = "SELECT COUNT(*) FROM %s WHERE %s" % \
***************
*** 141,146 ****
self.whereClauseForSelect(select, limit=0, order=0))
val = int(self.queryOne(q)[0])
- if self.debug:
- print "COUNT results:", val
return val
--- 184,187 ----
***************
*** 245,249 ****
# or classes freely, but keep the SQLObject class from accessing
# the database directly. This way no SQL is actually created
! # in SQLObject.
def _SO_update(self, so, values):
--- 286,290 ----
# or classes freely, but keep the SQLObject class from accessing
# the database directly. This way no SQL is actually created
! # in the SQLObject class.
def _SO_update(self, so, values):
***************
*** 320,323 ****
--- 361,365 ----
self._dbConnection = dbConnection
self._connection = dbConnection.getConnection()
+ self._dbConnection._setAutoCommit(self._connection, 0)
self.cache = CacheSet(cache=dbConnection.doCache)
***************
*** 329,340 ****
def queryOne(self, s):
! return self._dbConnection._queryAll(self._connection, s)
def commit(self):
self._connection.commit()
def rollback(self):
self._connection.rollback()
def __del__(self):
self.rollback()
--- 371,413 ----
def queryOne(self, s):
! return self._dbConnection._queryOne(self._connection, s)
!
! def queryInsertID(self, table, idName, names, values):
! return self._dbConnection._queryInsertID(
! self._connection, table, idName, names, values)
!
! def iterSelect(self, select):
! # @@: Bad stuff here, because the connection will be used
! # until the iteration is over, or at least a cursor from
! # the connection, which not all database drivers support.
! return self._dbConnection._iterSelect(
! self._connection, select, withConnection=self,
! keepConnection=True)
def commit(self):
+ if self._dbConnection.debug:
+ self._dbConnection.printDebug(self._connection, '', 'COMMIT')
self._connection.commit()
def rollback(self):
+ if self._dbConnection.debug:
+ self._dbConnection.printDebug(self._connection, '', 'ROLLBACK')
self._connection.rollback()
+ def __getattr__(self, attr):
+ """
+ If nothing else works, let the parent connection handle it.
+ Except with this transaction as 'self'. Poor man's
+ acquisition? Bad programming? Okay, maybe.
+ """
+ attr = getattr(self._dbConnection, attr)
+ try:
+ func = attr.im_func
+ except AttributeError:
+ return attr
+ else:
+ meth = new.instancemethod(func, self, self.__class__)
+ return meth
+
def __del__(self):
self.rollback()
***************
*** 343,346 ****
--- 416,421 ----
class MySQLConnection(DBAPI):
+ supportTransactions = False
+
def __init__(self, db, user, passwd='', host='localhost', **kw):
global MySQLdb
***************
*** 361,367 ****
q = self._insertSQL(table, names, values)
if self.debug:
! print 'QueryIns: %s' % q
c.execute(q)
! return c.insert_id()
def _queryAddLimitOffset(self, query, start, end):
--- 436,445 ----
q = self._insertSQL(table, names, values)
if self.debug:
! self.printDebug(conn, q, 'QueryIns')
c.execute(q)
! id = c.insert_id()
! if self.debugOutput:
! self.printDebug(conn, id, 'QueryIns', 'result')
! return id
def _queryAddLimitOffset(self, query, start, end):
***************
*** 429,437 ****
class PostgresConnection(DBAPI):
def __init__(self, dsn=None, host=None, db=None,
! user=None, passwd=None, autoCommit=1, **kw):
! global psycopg
! if psycopg is None:
! import psycopg
if not autoCommit and not kw.has_key('pool'):
# Pooling doesn't work with transactions...
--- 507,527 ----
class PostgresConnection(DBAPI):
+ supportTransactions = True
+
def __init__(self, dsn=None, host=None, db=None,
! user=None, passwd=None, autoCommit=1,
! usePygresql=False,
! **kw):
! global psycopg, pgdb
! if usePygresql:
! if pgdb is None:
! import pgdb
! self.pgmodule = pgdb
! else:
! if psycopg is None:
! import psycopg
! self.pgmodule = psycopg
!
! self.autoCommit = autoCommit
if not autoCommit and not kw.has_key('pool'):
# Pooling doesn't work with transactions...
***************
*** 452,457 ****
DBAPI.__init__(self, **kw)
def makeConnection(self):
! return psycopg.connect(self.dsn)
def _queryInsertID(self, conn, table, idName, names, values):
--- 542,553 ----
DBAPI.__init__(self, **kw)
+ def _setAutoCommit(self, conn, auto):
+ conn.autocommit(auto)
+
def makeConnection(self):
! conn = self.pgmodule.connect(self.dsn)
! if self.autoCommit:
! conn.autocommit(1)
! return conn
def _queryInsertID(self, conn, table, idName, names, values):
***************
*** 459,467 ****
q = self._insertSQL(table, names, values)
if self.debug:
! print 'QueryIns: %s' % q
c.execute(q)
c.execute('SELECT %s FROM %s WHERE oid = %s'
% (idName, table, c.lastoid()))
! return c.fetchone()[0]
def _queryAddLimitOffset(self, query, start, end):
--- 555,566 ----
q = self._insertSQL(table, names, values)
if self.debug:
! self.printDebug(conn, q, 'QueryIns')
c.execute(q)
c.execute('SELECT %s FROM %s WHERE oid = %s'
% (idName, table, c.lastoid()))
! id = c.fetchone()[0]
! if self.debugOutput:
! self.printDebug(conn, id, 'QueryIns', 'result')
! return id
def _queryAddLimitOffset(self, query, start, end):
***************
*** 555,558 ****
--- 654,659 ----
class SQLiteConnection(DBAPI):
+ supportTransactions = True
+
def __init__(self, filename, autoCommit=1, **kw):
global sqlite
***************
*** 568,571 ****
--- 669,675 ----
DBAPI.__init__(self, **kw)
+ def _setAutoCommit(self, conn, auto):
+ conn.autocommit = auto
+
def makeConnection(self):
return self._conn
***************
*** 575,582 ****
q = self._insertSQL(table, names, values)
if self.debug:
! print 'QueryIns: %s' % q
c.execute(q)
# lastrowid is a DB-API extension from "PEP 0249":
! return int(c.lastrowid)
def _queryAddLimitOffset(self, query, start, end):
--- 679,689 ----
q = self._insertSQL(table, names, values)
if self.debug:
! self.printDebug(conn, q, 'QueryIns')
c.execute(q)
# lastrowid is a DB-API extension from "PEP 0249":
! id = int(c.lastrowid)
! if self.debugOutput:
! self.printDebug(conn, id, 'QueryIns', 'result')
! return id
def _queryAddLimitOffset(self, query, start, end):
***************
*** 607,610 ****
--- 714,719 ----
class FirebirdConnection(DBAPI):
+ supportTransactions = False
+
def __init__(self, host, db, user='sysdba',
passwd='masterkey', autoCommit=1, **kw):
***************
*** 644,649 ****
qry = self._insertSQL(table, names, values)
if self.debug:
! print 'QueryIns: %s' % q
self.query(qry)
return new_key
--- 753,760 ----
qry = self._insertSQL(table, names, values)
if self.debug:
! self.printDebug(conn, q, 'QueryIns')
self.query(qry)
+ if self.debugOutput:
+ self.printDebug(conn, new_key, 'QueryIns', 'result')
return new_key
***************
*** 659,664 ****
match = self.limit_re.match(query)
- if self.debug:
- print query
if match and len(match.groups()) == 2:
return ' '.join([limit_str, match.group(1)])
--- 770,773 ----
***************
*** 855,858 ****
--- 964,969 ----
class DBMConnection(FileConnection):
+
+ supportTransactions = False
def __init__(self, path, **kw):
Index: SQLObject.py
===================================================================
RCS file: /cvsroot/sqlobject/SQLObject/SQLObject/SQLObject.py,v
retrieving revision 1.51
retrieving revision 1.52
diff -C2 -d -r1.51 -r1.52
*** SQLObject.py 6 Sep 2003 03:05:12 -0000 1.51
--- SQLObject.py 7 Sep 2003 00:49:03 -0000 1.52
***************
*** 378,383 ****
assert id is not None, 'None is not a possible id for %s' % cls.__name
! # When id is None, that means we are trying
! # to create a new object. This is done by the
# `new()` method.
if id is CreateNewSQLObject:
--- 378,383 ----
assert id is not None, 'None is not a possible id for %s' % cls.__name
! # When id is CreateNewSQLObject, that means we are trying to
! # create a new object. This is a contract of sorts with the
# `new()` method.
if id is CreateNewSQLObject:
***************
*** 388,391 ****
--- 388,394 ----
# column-values for the new row:
inst._SO_createValues = {}
+ if connection is not None:
+ inst._connection = connection
+ assert selectResults is None
return inst
***************
*** 507,511 ****
if column.alternateMethodName:
! func = eval('lambda cls, val: cls._SO_fetchAlternateID(%s, val)' % repr(column.dbName))
setattr(cls, column.alternateMethodName, classmethod(func))
--- 510,514 ----
if column.alternateMethodName:
! func = eval('lambda cls, val, connection=None: cls._SO_fetchAlternateID(%s, val, connection=connection)' % repr(column.dbName))
setattr(cls, column.alternateMethodName, classmethod(func))
***************
*** 663,667 ****
dbNames = [col.dbName for col in self._SO_columns]
if not selectResults:
! selectResults = (connection or self._connection)._SO_selectOne(self, dbNames)
if not selectResults:
raise SQLObjectNotFound, "The object %s by the ID %s does not exist" % (self.__class__.__name__, self.id)
--- 666,670 ----
dbNames = [col.dbName for col in self._SO_columns]
if not selectResults:
! selectResults = self._connection._SO_selectOne(self, dbNames)
if not selectResults:
raise SQLObjectNotFound, "The object %s by the ID %s does not exist" % (self.__class__.__name__, self.id)
***************
*** 809,813 ****
# Here's where an INSERT is finalized.
# These are all the column values that were supposed
! # to be set, but weren't.
setters = self._SO_createValues.items()
# Here's their database names:
--- 812,816 ----
# Here's where an INSERT is finalized.
# These are all the column values that were supposed
! # to be set, but were delayed until now:
setters = self._SO_createValues.items()
# Here's their database names:
***************
*** 815,818 ****
--- 818,823 ----
values = [v[1] for v in setters]
# Get rid of _SO_create*, we aren't creating anymore.
+ # Doesn't have to be threadsafe because we're still in
+ # new(), which doesn't need to be threadsafe.
del self._SO_createValues
del self._SO_creating
***************
*** 830,835 ****
return getID(obj)
! def _SO_fetchAlternateID(cls, dbIDName, value):
! result = cls._connection._SO_selectOneAlt(
cls,
[cls._idName] +
--- 835,840 ----
return getID(obj)
! def _SO_fetchAlternateID(cls, dbIDName, value, connection=None):
! result = (connection or cls._connection)._SO_selectOneAlt(
cls,
[cls._idName] +
***************
*** 839,843 ****
if not result:
raise SQLObjectNotFound, "The %s by alternateID %s=%s does not exist" % (cls.__name__, dbIDName, repr(value))
! obj = cls(result[0])
if not obj._cacheValues:
obj._SO_writeLock.acquire()
--- 844,851 ----
if not result:
raise SQLObjectNotFound, "The %s by alternateID %s=%s does not exist" % (cls.__name__, dbIDName, repr(value))
! if connection:
! obj = cls(result[0], connection=connection)
! else:
! obj = cls(result[0])
if not obj._cacheValues:
obj._SO_writeLock.acquire()
***************
*** 847,863 ****
_SO_fetchAlternateID = classmethod(_SO_fetchAlternateID)
- # 3-03 @@: Should this have a connection argument?
def select(cls, clause=None, clauseTables=None,
orderBy=NoDefault, groupBy=None, limit=None,
! lazyColumns=False, reversed=False):
return SelectResults(cls, clause, clauseTables=clauseTables,
orderBy=orderBy, groupBy=groupBy,
limit=limit, lazyColumns=lazyColumns,
! reversed=reversed)
select = classmethod(select)
! def selectBy(cls, **kw):
return SelectResults(cls,
! cls._connection._SO_columnClause(cls, kw))
selectBy = classmethod(selectBy)
--- 855,873 ----
_SO_fetchAlternateID = classmethod(_SO_fetchAlternateID)
def select(cls, clause=None, clauseTables=None,
orderBy=NoDefault, groupBy=None, limit=None,
! lazyColumns=False, reversed=False,
! connection=None):
return SelectResults(cls, clause, clauseTables=clauseTables,
orderBy=orderBy, groupBy=groupBy,
limit=limit, lazyColumns=lazyColumns,
! reversed=reversed,
! connection=connection)
select = classmethod(select)
! def selectBy(cls, connection=None, **kw):
return SelectResults(cls,
! cls._connection._SO_columnClause(cls, kw),
! connection=connection)
selectBy = classmethod(selectBy)
***************
*** 988,991 ****
--- 998,1003 ----
orderBy = self._mungeOrderBy(orderBy)
self.ops['dbOrderBy'] = orderBy
+ if ops.has_key('connection') and ops['connection'] is None:
+ del ops['connection']
def _mungeOrderBy(self, orderBy):
|