dabo Commit
Revision 7278
Date: 2012-10-13 11:39:16 -0700 (Sat, 13 Oct 2012)
Author: Nate
Trac: http://trac.dabodev.com/changeset/7278

Changed:
U   trunk/dabo/biz/dBizobj.py
U   trunk/dabo/db/dBackend.py
U   trunk/dabo/db/dConnectInfo.py
U   trunk/dabo/db/dConnection.py
U   trunk/dabo/db/dbMySQL.py

Log:
MySQL will auto-close connections after a certain period.  If you try to 
execute a statement, it will through a 2006 operational error.  You can call 
the connections ping method to reestablish the connection and try to execute 
again.  I wanted to overwrite the standard dabo cursor (dCursorMixin) with a 
MySQL specific cursor.  However this wasn't possible without a framework 
change.  We already had a facility in place to grab the specific backend dbapi 
cursor class from the dBackend object.  I extended that model so that instead 
of hardcoding the main dCursorMixin class in the object we can grab it from the 
dBackend object.  It will auto-default to dCursorMixin if we don't override it 
in the subclass.

Tested with MySQL and the SQLite adapter and it works.  We should really 
consider moving the CreateCursor and _getCursorClass methods on dBizobj down 
into the db layer?\226?\128?\166

Diff:
Modified: trunk/dabo/biz/dBizobj.py
===================================================================
--- trunk/dabo/biz/dBizobj.py   2012-10-11 19:13:53 UTC (rev 7277)
+++ trunk/dabo/biz/dBizobj.py   2012-10-13 18:39:16 UTC (rev 7278)
@@ -21,8 +21,6 @@
 
 class dBizobj(dObject):
        """ The middle tier, where the business logic resides."""
-       # Class to instantiate for the cursor object
-       dCursorMixinClass = dCursorMixin
        # Tell dObject that we'll call before and afterInit manually:
        _call_beforeInit, _call_afterInit, _call_initProperties = False, False, 
False
 
@@ -131,6 +129,7 @@
                if conn:
                        # Base cursor class : the cursor class from the db api
                        self.dbapiCursorClass = 
self._cursorFactory.getDictCursorClass()
+                       self.dCursorMixinClass = 
self._cursorFactory.getMainCursorClass()
                        # If there are any problems in the createCursor 
process, an
                        # exception will be raised in that method.
                        self.createCursor()
@@ -459,7 +458,7 @@
                # in some out-of-context child cursor, but that should be rare. 
In the
                # common case, all the changes would have already been made in 
the above
                # block, and isAnyChanged() will return False very quickly in 
that case.
-               if self.isAnyChanged(): 
+               if self.isAnyChanged():
                        try:
                                self.scan(self.save, startTransaction=False,
                                                
saveTheChildren=saveTheChildren, scanRequeryChildren=False)
@@ -561,7 +560,7 @@
                # in some out-of-context child cursor, but that should be rare. 
In the
                # common case, all the cancellations would have already 
happened in the
                # above block, and isAnyChanged() will return False very 
quickly.
-               if self.isAnyChanged(): 
+               if self.isAnyChanged():
                        self.scanChangedRows(self.cancel, allCursors=False,
                                        includeNewUnchanged=True, 
cancelTheChildren=cancelTheChildren,
                                        ignoreNoRecords=ignoreNoRecords, 
reverse=True)
@@ -1313,7 +1312,7 @@
                If the operator is specified, it will be used literally in the 
evaluation instead of the
                equals sign, unless it is one of the following strings, which 
will be interpreted
                as indicated:
-                       
+
                        | eq, equals: =
                        | ne, nequals: !=
                        | gt: >
@@ -1355,9 +1354,9 @@
                """Allows you to filter by any valid Python expression.
 
                Use the field alias names, for example::
-               
+
                        biz.filterByExpression('cust_name[0].lower() = 'a')
-               
+
                where cust_name is a field alias name in this record.
                """
                self._CurrentCursor.filterByExpression(expr)
@@ -1444,7 +1443,7 @@
                an error message that describes the reason why the data is not
                valid; this message will be propagated back up to the UI where 
it can
                be displayed to the user so that they can correct the problem.
-               
+
                Example::
 
                        if not myNonEmptyField:
@@ -1515,7 +1514,7 @@
                For internal use only! Should never be called from a 
developer's code.
                Its purpose is to keep child cursor in sync with parent cursor.
                The updateChildren parameter meaning:
-               
+
                        | None  - the fastest one, doesn't update parent nor 
requery child cursor
                        | False - update child cursor with current parent
                        | True  - do both, update child cursor's parent and 
requery child cursor.
@@ -1598,7 +1597,7 @@
 
                If incremental is True (default is False), then we only compare 
the first
                characters up until the length of val.
-               
+
                Returns the RowNumber of the found record, or -1 if no match 
found.
                """
                ret = self._CurrentCursor.seek(val, fld, caseSensitive, near,
@@ -2117,7 +2116,7 @@
                """
                Gets the structure of the DataSource table. Returns a list
                of 3-tuples, where the 3-tuple's elements are:
-               
+
                        | 0: the field name (string)
                        | 1: the field type ('I', 'N', 'C', 'M', 'B', 'D', 'T')
                        | 2: boolean specifying whether this is a pk field.
@@ -2129,7 +2128,7 @@
                """
                Gets the structure of the DataSource table. Returns a list
                of 3-tuples, where the 3-tuple's elements are:
-               
+
                        | 0: the field name (string)
                        | 1: the field type ('I', 'N', 'C', 'M', 'B', 'D', 'T')
                        | 2: boolean specifying whether this is a pk field.
@@ -2576,7 +2575,7 @@
                Hook method called after a field's value has been set.
 
                Your hook method needs to accept two arguments:
-               
+
                        | -> fld : The name of the changed field.
                        | -> row : The RowNumber of the changed field.
 
@@ -3105,7 +3104,7 @@
                        _("""If set, treated as cursor real table name where 
DataSource
                        is an alias     for it. This allows coexistence of many 
business objects
                        with same data source on single form. (str)
-                       
+
                        Example:
                                class StockBase(dBizobj):
                                        def initProperties(self):

Modified: trunk/dabo/db/dBackend.py
===================================================================
--- trunk/dabo/db/dBackend.py   2012-10-11 19:13:53 UTC (rev 7277)
+++ trunk/dabo/db/dBackend.py   2012-10-13 18:39:16 UTC (rev 7278)
@@ -12,9 +12,9 @@
 from dabo.db import dTable
 from dNoEscQuoteStr import dNoEscQuoteStr
 from dabo.lib.utils import ustr
+from dCursorMixin import dCursorMixin
 
 
-
 class dBackend(dObject):
        """Abstract class inherited by the specific Dabo database connectors."""
        # Pattern for determining if a function is present in a string
@@ -53,7 +53,11 @@
                """override in subclasses"""
                return None
 
+       def getMainCursorClass(self):
+               """override in subclasses if they need something other than 
dCursorMixin"""
+               return dCursorMixin
 
+
        def getCursor(self, cursorClass):
                """override in subclasses if necessary"""
                return cursorClass(self._connection)

Modified: trunk/dabo/db/dConnectInfo.py
===================================================================
--- trunk/dabo/db/dConnectInfo.py       2012-10-11 19:13:53 UTC (rev 7277)
+++ trunk/dabo/db/dConnectInfo.py       2012-10-13 18:39:16 UTC (rev 7278)
@@ -86,6 +86,13 @@
                        return None
 
 
+       def getMainCursorClass(self):
+               try:
+                       return self._backendObject.getMainCursorClass()
+               except TypeError:
+                       return None
+
+
        def encrypt(self, val):
                if self.Application:
                        return self.Application.encrypt(val)

Modified: trunk/dabo/db/dConnection.py
===================================================================
--- trunk/dabo/db/dConnection.py        2012-10-11 19:13:53 UTC (rev 7277)
+++ trunk/dabo/db/dConnection.py        2012-10-13 18:39:16 UTC (rev 7278)
@@ -44,6 +44,10 @@
                return self._connectInfo.getDictCursorClass()
 
 
+       def getMainCursorClass(self):
+               return self._connectInfo.getMainCursorClass()
+
+
        def getCursor(self, cursorClass):
                return self.getBackendObject().getCursor(cursorClass)
 

Modified: trunk/dabo/db/dbMySQL.py
===================================================================
--- trunk/dabo/db/dbMySQL.py    2012-10-11 19:13:53 UTC (rev 7277)
+++ trunk/dabo/db/dbMySQL.py    2012-10-13 18:39:16 UTC (rev 7278)
@@ -7,9 +7,19 @@
 import dabo.dException as dException
 from dNoEscQuoteStr import dNoEscQuoteStr as dNoEQ
 from dabo.lib.utils import ustr
+from dCursorMixin import dCursorMixin
 
 
+class MySQLAutoReconnectCursor(dCursorMixin):
+       def execute(self, sql, params=None, errorClass=None, 
convertQMarks=False):
+               from MySQLdb import OperationalError
+               try:
+                       super(MySQLAutoReconnectCursor, self).execute(sql, 
params=params, errorClass=OperationalError, convertQMarks=convertQMarks)
+               except OperationalError:
+                       self.connection.ping(True)
+                       super(MySQLAutoReconnectCursor, self).execute(sql, 
params=params, errorClass=None, convertQMarks=convertQMarks)
 
+
 class MySQL(dBackend):
        """Class providing MySQL connectivity. Uses MySQLdb."""
 
@@ -79,10 +89,19 @@
 
 
        def getDictCursorClass(self):
-               import MySQLdb.cursors as cursors
-               return cursors.DictCursor
+               return MySQLdb.cursors.DictCursor
 
+       def getMainCursorClass(self):
+               return MySQLAutoReconnectCursor
 
+       #def getCursor(self, CursorClass):
+       #       raise
+       #       print "CursorClass is: %s" % CursorClass
+       #       if CursorClass == dCrusorMixin:
+       #               return MySQLAutoReconnectCursor(self._connection)
+       #       else:
+       #               return CursorClass(self._connection)
+
        def beginTransaction(self, cursor):
                """ Begin a SQL transaction."""
                cursor.execute("START TRANSACTION")



_______________________________________________
Post Messages to: [email protected]
Subscription Maintenance: http://leafe.com/mailman/listinfo/dabo-dev
Searchable Archives: http://leafe.com/archives/search/dabo-dev
This message: 
http://leafe.com/archives/byMID/[email protected]

Reply via email to