[Zope-DB] CVS: Products/DCOracle2/DCOracle2 - DCOracle2.py:1.72

Matthew T. Kromer matt@zope.com
Mon, 8 Oct 2001 10:26:02 -0400


Update of /cvs-repository/Products/DCOracle2/DCOracle2
In directory cvs.zope.org:/tmp/cvs-serv17149/DCOracle2

Modified Files:
	DCOracle2.py 
Log Message:
Updated patch for Christopher Jenkins; execute() with initial NULLs for non
string columns requires a reprepare on subsequent execution with same statement
handle due to type mismatches in the bind handles.


=== Products/DCOracle2/DCOracle2/DCOracle2.py 1.71 => 1.72 ===
 #
 
+_nonullmap = []
+
 class cursor:
     
     _cursor = None
@@ -641,6 +643,7 @@
         self._connection = connection
         self._sizes = []
         self._mapproc = {}
+        self._nullmap = None
 
 
     # NONAPI
@@ -689,6 +692,7 @@
         self._cursor.close()
         self._cursor = None
         self._connection = None
+        self._nullmap = None
 
     # NONAPI
     def isOpen(self):
@@ -698,20 +702,31 @@
     def prepare(self, statement):
         self._cursor.prepare(statement)
         self._operation = statement
+        self._nullmap = {}
 
     # execute
     #
     # the special keyword parameter is '__plist' which the stored
     # procedure __call__ method uses to invoke with a list of paramters
+
+    #
+    # The _nonullmap and _nullmap business is to force a re-prepare when
+    # an initial bind was done with a NULL parameter (None) which gets bound
+    # as SQLT_STR.  The nullmap maps which parameters are None -- after an
+    # initial execute, if no entries are in the map, it is replaced with
+    # _nonullmap, an empty list; which is a signal to stop checking it until
+    # the next normal prepare.
+    #
     def execute(self, operation=None, *params, **kw):
         if self._cursor is None:
             raise InterfaceError,"cursor is closed"
 
         if operation is None: operation = self._operation
 
-        if self._operation != operation:
+        if self._operation != operation or self._nullmap is None:
             self._cursor.prepare(operation)
             self._operation = operation
+            self._nullmap = {}
 
             if self._prefetch:
                 apply(self._cursor.setPrefetch, self._prefetch)
@@ -745,6 +760,8 @@
         # Bind positional parameters
         for p in params:
             i = i + 1
+            if p is None and self._nullmap is not _nonullmap:
+                self._nullmap[i] = 1
             #print "binding %d with %s" % (i, p)
             if type(p) == types.InstanceType and issubclass(p.__class__,
                 TypeCoercion):
@@ -757,6 +774,8 @@
             ck = ":" + key
             #print "binding %s as %s" % (ck, kw[key])
             p = kw[key]
+            if p is None and self._nullmap is not _nonullmap:
+                self._nullmap[key] = 1
             if type(p) == types.InstanceType and issubclass(p.__class__,
                 TypeCoercion):
                 self._cursor.bindbyname(ck, p.value, p.type)
@@ -771,6 +790,12 @@
             if self._operation != so: self._operation = so # Force rebind
         if not result == 1:
             self.description = None
+
+        if len(self._nullmap) > 0:
+            self._nullmap = None
+        else:
+            self._nullmap = _nonullmap
+
         return result
 
     def executemany(self, operation, params):
@@ -781,6 +806,7 @@
                 self._cursor.prepare(operation)
                 self._operation = operation
                 prepared = 1
+                self._nullmap = None
 
         # Potential note for named parameters:  coerce all positional 
         # parameters to named parameters 'by number' e.g. using the postion
@@ -826,6 +852,7 @@
             for c in xrange(columns):
                 if notnull[c] == 0:
                     self._cursor.prepare(operation)
+                    self._nullmap = None
                     self._operation = self._operation + " " # force rebind next
                     break