Re: Segentation faults when connecting to SQL Server 2000

Kevin Jacobs <[email protected]>
Newsgroups gmane.comp.python.sybase
Message-ID <[email protected]>
On Tue, 10 Sep 2002, Harri Pasanen wrote:
> I experienced similar things.  Probably if you change the query order, it will 
> still be so that the first one works.   So the problem is that the connection 
> / cursors keep some state that throws it off balance.
> 
> I managed to work around this by closing the cursor and reallocating a new one 
> for the next query.  And having opened the connection with 'auto_commit=1' 
> also helped.  

Attached is a _very_ experimental patch that re-works some of the connection
state logic and locking in the Sybase.py module.  Please apply it to an
original copy of Sybase.py, and let me know how it goes.  With it, I am able
to run many queries in sequence very reliably.  Unfortunately, I don't have
a Sybase installation to test, so I don't know exactly where the FreeTDS
implementation of libCT seems to be diverging from what must obviously be
robust for many Sybase users.

Good luck,
-Kevin

--
Kevin Jacobs
The OPAL Group - Enterprise Systems Architect
Voice: (216) 986-0710 x 19         E-mail: [email protected]
Fax:   (216) 986-0714              WWW:    http://www.theopalgroup.com
diff (text/plain, 5.6 KB)
--- /home/jacobs/projects/MSSQL/sybase-0.35pre1/Sybase.py	Sun Apr 21 02:00:03 2002
+++ Sybase.py	Mon Jul 15 09:21:38 2002
@@ -136,7 +136,7 @@
     raise DatabaseError(_fmt_client(msg))
 
 def _servermsg_cb(ctx, conn, msg):
-    if msg.msgnumber not in (5701,):
+    if msg.msgnumber not in (5701,5703):
         raise DatabaseError(_fmt_server(msg))
 
 def _row_bind(cmd, count = 1):
@@ -231,6 +231,7 @@
         self._owner = owner
         self._state = _CUR_IDLE
         self._lock_count = 0
+        self._channel_lock_held = 0
         self._lock()
         try:
             status, self._cmd = owner._conn.ct_cmd_alloc()
@@ -258,12 +259,28 @@
     def _unlock(self):
         self._owner._lock.release()
         self._lock_count = self._lock_count - 1
+        #assert not self._channel_lock_held or self._lock_count > 0
+
+    def _lock_channel(self):
+        assert self._lock_count > 0
+        assert self._state == _CUR_IDLE
+        assert not self._channel_lock_held
+        self._lock()
+        self._channel_lock_held = 1
+
+    def _unlock_channel(self):
+        assert self._channel_lock_held
+        assert self._state in (_CUR_IDLE,_CUR_CLOSED)
+        assert self._lock_count > 1
+        self._channel_lock_held = 0
+        self._unlock()
 
     def _raise_error(self, exc, text):
         if self._state not in (_CUR_IDLE, _CUR_CLOSED):
-            if self._owner._conn.ct_cancel(CS_CANCEL_ALL) == CS_SUCCEED:
+            if self._owner and self._owner._conn and \
+               self._owner._conn.ct_cancel(CS_CANCEL_ALL) == CS_SUCCEED:
                 self._state = _CUR_IDLE
-                self._unlock()
+                self._unlock_channel()
         raise exc(text)
 
     def callproc(self, name, params = ()):
@@ -279,7 +296,7 @@
                 # At the start of a command acquire an extra lock -
                 # when the cursor is idle again the extra lock will be
                 # released.
-                self._lock()
+                self._lock_channel()
                 status = self._cmd.ct_command(CS_RPC_CMD, name)
                 if status != CS_SUCCEED:
                     self._raise_error(Error, 'ct_command')
@@ -314,9 +331,13 @@
             if self._state != _CUR_IDLE:
                 status = self._cmd.ct_cancel(CS_CANCEL_ALL)
                 if status == CS_SUCCEED:
-                    self._unlock()
+                    self._state = _CUR_IDLE
+                    self._unlock_channel()
+                else:
+                    self._raise_error(Error, 'ct_cancel')
             self._cmd = None
             self._state = _CUR_CLOSED
+            assert not self._channel_lock_held
         finally:
             self._unlock()
 
@@ -332,7 +353,7 @@
             # At the start of a command acquire an extra lock - when
             # the cursor is idle again the extra lock will be
             # released.
-            self._lock()
+            self._lock_channel()
             self._cmd.ct_command(CS_LANG_CMD, sql)
             for name, value in params.items():
                 buf = DataBuf(value)
@@ -363,7 +384,7 @@
         self._lock()
         try:
             if self._state == _CUR_IDLE:
-                return
+                self._raise_error(ProgrammingError, 'no result set pending')
             if self._state == _CUR_CLOSED:
                 self._raise_error(ProgrammingError, 'cursor is closed')
             if self._state == _CUR_FETCHING:
@@ -373,7 +394,7 @@
                     status = self._cmd.ct_cancel(CS_CANCEL_ALL)
                     if status == CS_SUCCEED:
                         self._state = _CUR_IDLE
-                        self._unlock()
+                        self._unlock_channel()
                     raise
                 if row:
                     return row
@@ -444,10 +465,10 @@
         pass
 
     def _fetch_rowcount(self):
+        self.rowcount = -1
         status, result = self._cmd.ct_results()
         if status == CS_END_RESULTS:
-            self._state = _CUR_IDLE
-            self._unlock()
+            self.rowcount = 0
             return
         elif status != CS_SUCCEED:
             self._raise_error(Error, 'ct_results')
@@ -459,11 +480,14 @@
             self._raise_error(Error, 'ct_results')
 
     def _start_results(self):
+        self.rowcount = -1
         while 1:
             status, result = self._cmd.ct_results()
             if status == CS_END_RESULTS:
-                self._state = _CUR_IDLE
-                self._unlock()
+                self.rowcount = 0
+                if self._state != _CUR_IDLE:
+                  self._state = _CUR_IDLE
+                  self._unlock_channel()
                 return
             elif status != CS_SUCCEED:
                 self._raise_error(Error, 'ct_results')
@@ -481,6 +505,7 @@
             elif result in (CS_CMD_DONE, CS_CMD_SUCCEED):
                 status, self.rowcount = self._cmd.ct_res_info(CS_ROW_COUNT)
                 if status != CS_SUCCEED:
+                    self.rowcount = -1
                     self._raise_error(Error, 'ct_res_info')
             else:
                 self._raise_error(Error, 'ct_results')
@@ -562,9 +587,10 @@
     def close(self):
         '''DBI-API Connection.close()
         '''
-        conn = self._conn
         self._lock.acquire()
         try:
+            conn = self._conn
+            self._conn = None
             status, result = conn.ct_con_props(CS_GET, CS_CON_STATUS)
             if status != CS_SUCCEED:
                 self._raise_error(Error, 'ct_con_props')
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.