# -*- coding: iso-8859-1 -*-
#-----------------------------------------------------------------------------
# Modeling Framework: an Object-Relational Bridge for python
#
# Copyright (c) 2001-2004 Sbastien Bigaret <sbigaret@users.sourceforge.net>
# All rights reserved.
#
# This file is part of the Modeling Framework.
#
# This code is distributed under a "3-clause BSD"-style license;
# see the LICENSE file for details.
#-----------------------------------------------------------------------------
"""
AbstractDBAPI2Adaptor
CVS information
$Id: AbstractDBAPI2AdaptorChannel.py 933 2004-08-02 19:58:54Z sbigaret $
"""
__version__='$Revision: 933 $'[11:-2]
from Modeling.logging import db_info,db_trace,db_error,warn
import sys
# Framework
from Modeling.AdaptorChannel import AdaptorChannel
from Modeling.Adaptor import GeneralAdaptorException
# Interfaces
from Modeling.interfaces.AdaptorChannel import IAdaptorChannel
class AbstractDBAPI2AdaptorChannel(AdaptorChannel):
"""
Your inheriting concrete Adaptor class **must** override:
- primaryKeysForNewRowsWithEntity()
You normally do not need to override other methods. In particular,
dbAPI_cursor() does not need to be overridden --but you will use it to
access the underlying cursor.
"""
__implements__ = (IAdaptorChannel,)
# for testing purposes only
_count_for_execute=0
def __init__(self, anAdaptorContext):
"See Modeling.interfaces.AdaptorChannel for details"
AdaptorChannel.__init__.im_func(self, anAdaptorContext)
self.__cursor=None
self.__attrToFetch=[]
self.__isFetchInProgress=0
def attributesToFetch(self):
"See Modeling.interfaces.AdaptorChannel for details"
return tuple(self.__attrToFetch)
def cancelFetch(self):
"See Modeling.interfaces.AdaptorChannel for details"
self.__isFetchInProgress=0
self.__attrToFetch=[]
def closeChannel(self):
"See Modeling.interfaces.AdaptorChannel for details"
db_trace('Closing adaptorChannel %s (%s)'%(str(self), hex(id(self))))
self.cancelFetch() ##
if self.__cursor is not None:
# This may happen when, e.g., saveChanges() is sent to an EditingContext
# but no changes needs to be done hence the channel was not opened
self.__cursor.close()
self.__cursor=None
self._adaptorContext.adaptorChannelDidClose(self)
else:
warn('Closing an already closed channel!')
def deleteRowsDescribedByQualifier(self, aQualifier, anEntity):
"""
Builds the DELETE SQLExpression for 'aQualifier' and 'anEntity' ans
executes it. Returns the number of rows that were deleted.
Raises GeneralAdaptorException if the channel is not opened, or if it is
fetching, or if its AdaptorContext has no transaction in progress.
See Modeling.interfaces.AdaptorChannel for details
"""
openTrans=self._adaptorContext.hasOpenTransaction()
if not openTrans:
raise GeneralAdaptorException, "Invalid state: channel's context has no transaction in progress"
if not self.isOpen():
raise GeneralAdaptorException, 'Invalid state: channel is not opened'
#if self.isFetchInProgress():
# raise GeneralAdaptorException, 'Invalid state: channel has a fetch in progress'
sqlExpr=self.adaptorContext().adaptor().expressionClass()(anEntity)
sqlExpr.prepareDeleteExpressionForQualifier(aQualifier)
statement=sqlExpr.statement()
db_info('Evaluating: %s'%statement)
try:
self.__cursor.execute(statement)
self.__class__._count_for_execute+=1
except:
exctype, value = sys.exc_info()[:2]
msg="Couldn't evaluate expression %s. Reason: %s:%s"%(statement, exctype, value)
db_error(msg)
raise GeneralAdaptorException, msg
#import pdb ; pdb.set_trace()
return self.__cursor.rowcount
def describeModelWithTableNames(self, tableNames):
"See Modeling.interfaces.AdaptorChannel for details"
self.__unimplemented__()
def describeResults(self):
"See Modeling.interfaces.AdaptorChannel for details"
self.__unimplemented__()
def describeStoredProcedureNames(self):
"See Modeling.interfaces.AdaptorChannel for details"
self.__unimplemented__()
def describeTableNames(self):
"See Modeling.interfaces.AdaptorChannel for details"
self.__unimplemented__()
def evaluateExpression(self, anSQLexpression):
"See Modeling.interfaces.AdaptorChannel for details"
#
openTrans=self._adaptorContext.hasOpenTransaction()
if not openTrans:
#if self.isFetchInProgress():
# raise GeneralAdaptorException, 'Invalid state: channel has a fetch in progress'
self._adaptorContext.beginTransaction()
#self.__isFetchInProgress=1
wasOpened=self.isOpen()
if not wasOpened: self.openChannel()
statement=anSQLexpression.statement()
db_info('Evaluating: %s'%statement)
try:
self.__cursor.execute(statement)
self.__class__._count_for_execute+=1
except:
exctype, value = sys.exc_info()[:2]
msg="Couldn't evaluate expression %s. Reason: %s:%s"%(statement, exctype, value)
db_error(msg)
if not openTrans:
self._adaptorContext.rollbackTransaction()
#if not wasOpened: self.closeChannel()
#self.cancelFetch()
raise GeneralAdaptorException, msg
if not openTrans:
self._adaptorContext.commitTransaction()
#self.cancelFetch() # No!!!! Wait for fetchRow() to do that!
#if not wasOpened: self.closeChannel()
def executeStoredProcedure(self, aStoredProcedure, values):
"See Modeling.interfaces.AdaptorChannel for details"
self.__unimplemented__()
def fetchRow(self):
"See Modeling.interfaces.AdaptorChannel for details"
#if not self.isFetchInProgress():
# raise GeneralAdaptorException, 'Invalid state: channel has no fetch in progress'
res=self.__cursor.fetchone() # also has: description
dict=None
if res is None:
self.cancelFetch()
else: # build dictionary
dict={}; idx=0
for key in map(lambda o: o.name(), self.attributesToFetch()):
dict[key]=res[idx]
idx+=1
db_trace('rowcount: %i Returning: %s'%(self.__cursor.rowcount, str(dict)))
return dict
def insertRow(self, row, anEntity):
"See Modeling.interfaces.AdaptorChannel for details"
sqlExpr=self.adaptorContext().adaptor().expressionClass()(anEntity)
sqlExpr.prepareInsertExpressionWithRow(row)
statement=sqlExpr.statement()
db_info('Evaluating: %s'%statement)
try:
self.__cursor.execute(statement)
self.__class__._count_for_execute+=1
except:
exctype, value = sys.exc_info()[:2]
msg="Couldn't evaluate expression %s. Reason: %s:%s"%(statement, exctype, value)
db_error(msg)
raise GeneralAdaptorException, msg
def isFetchInProgress(self):
"See Modeling.interfaces.AdaptorChannel for details"
return self.__isFetchInProgress
def isOpen(self):
"See Modeling.interfaces.AdaptorChannel for details"
if self.__cursor: return 1
else: return 0
def openChannel(self):
"See Modeling.interfaces.AdaptorChannel for details"
if not self.__cursor: # silently ignores already opened channel
db_trace('Opening channel %s (%s)'%(str(self), hex(id(self))))
self.__cursor=self.adaptorContext().dbAPI_cursorForAdaptorChannel()
def primaryKeysForNewRowsWithEntity(self, count, anEntity):
"""
Generates and returns a list of dictionaries, whose keys are
PK attributes' names and values the corresponding PK value.
If 'anEntity' defines more than one PK (compound primary key), returns an
empty list.
Parameters:
count -- the number of primary keys to generate
anEntity -- the entity for which PKs are generated
Subclasses should override this method without calling the superclass'
implementation.
This method is correlated with the implementation of your concrete
SchemaGeneration's methods:
- primaryKeySupportStatementsForEntityGroup()
- dropPrimaryKeySupportStatementsForEntityGroup()
See also: Modeling.AdaptorChannel.primaryKeysForNewRowsWithEntity()
"""
__abstract__()
def returnValuesForLastStoredProcedureInvocation(self):
"See Modeling.interfaces.AdaptorChannel for details"
self.__unimplemented__()
def rowCountForSelectAttributes(self, attributes, aFetchSpecification,
shouldLock, anEntity):
"""
"""
self.__isFetchInProgress=1
try:
self.setAttributesToFetch(attributes)
sqlExpr=self.adaptorContext().adaptor().expressionClass()(anEntity)
sqlExpr.prepareSelectCountExpressionWithAttributes(attributes,
shouldLock,
aFetchSpecification)
statement=sqlExpr.statement()
db_info('Evaluating: %s'%statement)
try:
self.__cursor.execute(statement)
self.__class__._count_for_execute+=1
except:
exctype, value = sys.exc_info()[:2]
msg="Couldn't evaluate expression %s. Reason: %s:%s"%(statement, exctype, value)
db_error(msg)
raise GeneralAdaptorException, msg
return self.__cursor.fetchone()[0]
finally:
self.cancelFetch()
def selectAttributes(self, attributes, aFetchSpecification, shouldLock,
anEntity):
"See Modeling.interfaces.AdaptorChannel for details"
self.__isFetchInProgress=1
self.setAttributesToFetch(attributes)
sqlExpr=self.adaptorContext().adaptor().expressionClass()(anEntity)
sqlExpr.prepareSelectExpressionWithAttributes(attributes, shouldLock,
aFetchSpecification)
statement=sqlExpr.statement()
db_info('Evaluating: %s'%statement)
try:
self.__cursor.execute(statement)
self.__class__._count_for_execute+=1
except:
exctype, value = sys.exc_info()[:2]
msg="Couldn't evaluate expression %s. Reason: %s:%s"%(statement, exctype, value)
db_error(msg)
self.cancelFetch()
raise GeneralAdaptorException, msg
def setAttributesToFetch(self, attributes):
"See Modeling.interfaces.AdaptorChannel for details"
self.__attrToFetch=attributes
def updateValuesInRowsDescribedByQualifier(self, row, aQualifier, anEntity):
"""
Builds the UPDATE SQLExpression for 'row', 'aQualifier' and 'anEntity' and
executes it.
Returns the number of rows that were updated.
Raises GeneralAdaptorException if the channel is not opened, or if it is
fetching, or if its AdaptorContext has no transaction in progress.
See Modeling.interfaces.AdaptorChannel for details
"""
#if self.isFetchInProgress():
# raise GeneralAdaptorException, 'Invalid state: channel has a fetch in progress'
#self.__isFetchInProgress=1
openTrans=self._adaptorContext.hasOpenTransaction()
if not openTrans:
raise GeneralAdaptorException, "Invalid state: channel's context has no transaction in progress"
if not self.isOpen():
raise GeneralAdaptorException, 'Invalid state: channel is not opened'
sqlExpr=self.adaptorContext().adaptor().expressionClass()(anEntity)
sqlExpr.prepareUpdateExpressionWithRow(row, aQualifier)
statement=sqlExpr.statement()
db_info('Evaluating: %s'%statement)
try:
self.__cursor.execute(statement)
self.__class__._count_for_execute+=1
except:
exctype, value = sys.exc_info()[:2]
msg="Couldn't evaluate expression %s. Reason: %s:%s"%(statement, exctype, value)
db_error(msg)
raise GeneralAdaptorException, msg
return self.__cursor.rowcount
##def setDelegate(self, aDelegate):
## "See Modeling.interfaces.Adaptor for details"
## Adaptor.setDelegate.im_func(self, aDelegate)
def __unimplemented__(self):
raise 'Unimplemented', 'Unimplemented yet'
# Specific to the AbstractDBAPI2AdaptorChannel
def dbAPI_cursor(self):
return self.__cursor
def __abstract__():
raise 'Unimplemented', 'abstract method, should be implemented in subclasses'
|