<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Recent changes to 7: Collapsing two or more result sets / return status</title><link>https://sourceforge.net/p/python-sybase/patches/7/</link><description>Recent changes to 7: Collapsing two or more result sets / return status</description><atom:link href="https://sourceforge.net/p/python-sybase/patches/7/feed.rss" rel="self"/><language>en</language><lastBuildDate>Thu, 18 Feb 2010 12:19:25 -0000</lastBuildDate><atom:link href="https://sourceforge.net/p/python-sybase/patches/7/feed.rss" rel="self" type="application/rss+xml"/><item><title>Collapsing two or more result sets / return status</title><link>https://sourceforge.net/p/python-sybase/patches/7/</link><description>&lt;div class="markdown_content"&gt;&lt;p&gt;There is a problem retrieving data from cursor with two or more result sets. Let's assume we have following Transact-SQL procedure:&lt;/p&gt;
&lt;p&gt;create procedure SampleProc&lt;br /&gt;
as&lt;br /&gt;
begin&lt;br /&gt;
select 1 as col1, 2 as col2&lt;br /&gt;
union&lt;br /&gt;
select 11,12&lt;/p&gt;
&lt;p&gt;select 3 as col3, "4" as col4, 5 as col5&lt;br /&gt;
union&lt;br /&gt;
select 31,"41",51&lt;br /&gt;
end&lt;/p&gt;
&lt;p&gt;and corresponding python code to get first result set:&lt;/p&gt;
&lt;p&gt;import Sybase&lt;/p&gt;
&lt;p&gt;db=Sybase.connect('Sybase','user','pass','')&lt;br /&gt;
cur = db.cursor()&lt;br /&gt;
cur.execute('exec SampleProc')&lt;/p&gt;
&lt;p&gt;rs = cur.fetchall()&lt;br /&gt;
print rs&lt;/p&gt;
&lt;p&gt;I expect output like "[(1, 2), (11, 12)]", but it is a bit longer: "[(1, 2), (11, 12), (3, '4', 5), (31, '41', 51)]".&lt;br /&gt;
I.e. cursor has returned both results instead of first one.&lt;/p&gt;
&lt;p&gt;Patch makes some changes in class Cursor to fix it.&lt;br /&gt;
1. Method _start(): add private variable _empty_set to test if end of result set has been reached.&lt;/p&gt;
&lt;p&gt;def _start(self):&lt;br /&gt;
self._result_list = []&lt;br /&gt;
self._rownum = -1&lt;br /&gt;
self.rowcount = -1&lt;br /&gt;
self.description = None&lt;br /&gt;
self._empty_set = False&lt;/p&gt;
&lt;p&gt;status = self._cmd.ct_send()&lt;br /&gt;
if status != CS_SUCCEED:&lt;br /&gt;
self._raise_error(Error('ct_send'))&lt;br /&gt;
self._fetching = True&lt;br /&gt;
return self._mainloop()&lt;/p&gt;
&lt;p&gt;2. Method _row_result(): delete call of _mainloop() and check end of result set. _mainloop() is the cause of getting all result sets instead of one.&lt;/p&gt;
&lt;p&gt;def _row_result(self):&lt;br /&gt;
logical_result = []&lt;br /&gt;
if self._empty_set:&lt;br /&gt;
count = 0&lt;br /&gt;
else:&lt;br /&gt;
count = self._fetch_rows(self._bufs, logical_result)&lt;br /&gt;
self._rownum += count&lt;br /&gt;
self._result_list += logical_result&lt;br /&gt;
self._empty_set = count == 0&lt;/p&gt;
&lt;p&gt;3. Method fetchmany(): check if there is more rows in current result set.&lt;/p&gt;
&lt;p&gt;def fetchmany(self, num = -1):&lt;br /&gt;
'''DB-API Cursor.fetchmany()&lt;br /&gt;
'''&lt;br /&gt;
self._lock()&lt;br /&gt;
try:&lt;br /&gt;
if self._rownum == -1:&lt;br /&gt;
self._raise_error(Error('No result set'))&lt;br /&gt;
if num &amp;lt; 0:&lt;br /&gt;
num = self.arraysize&lt;br /&gt;
while num &amp;gt; self._rownum and self._fetching and not self._empty_set:&lt;br /&gt;
self._row_result()&lt;br /&gt;
res = self._result_list[0:num]&lt;br /&gt;
del self._result_list[0:num]&lt;br /&gt;
self._rownum -= num&lt;br /&gt;
return res&lt;br /&gt;
finally:&lt;br /&gt;
self._unlock()&lt;/p&gt;
&lt;p&gt;4. Method fetchall(): the very same modification.&lt;/p&gt;
&lt;p&gt;def fetchall(self):&lt;br /&gt;
'''DB-API Cursor.fetchall()&lt;br /&gt;
'''&lt;br /&gt;
self._lock()&lt;br /&gt;
try:&lt;br /&gt;
if self._rownum == -1:&lt;br /&gt;
self._raise_error(Error('No result set'))&lt;br /&gt;
while self._fetching and not self._empty_set:&lt;br /&gt;
self._row_result()&lt;br /&gt;
res = self._result_list&lt;br /&gt;
self._result_list = []&lt;br /&gt;
self._rownum = 0&lt;br /&gt;
return res&lt;br /&gt;
finally:&lt;br /&gt;
self._unlock()&lt;/p&gt;
&lt;p&gt;5. Method nextset(): reset _empty_set and return True if _mainloop get empty set. Query may return empty result set, in that case variable _result_list would contain empty list. Though result set should be returned to client.&lt;/p&gt;
&lt;p&gt;def nextset(self):&lt;br /&gt;
'''DB-API Cursor.nextset()&lt;br /&gt;
'''&lt;br /&gt;
self._lock()&lt;br /&gt;
try:&lt;br /&gt;
if not self._fetching:&lt;br /&gt;
return None&lt;br /&gt;
status = self._cmd.ct_cancel(CS_CANCEL_CURRENT)&lt;br /&gt;
self._empty_set = False&lt;br /&gt;
self._result_list = []&lt;br /&gt;
self._rownum = -1&lt;br /&gt;
self.rowcount = -1&lt;br /&gt;
self._mainloop()&lt;br /&gt;
if self._result_list or self._empty_set:&lt;br /&gt;
return True&lt;br /&gt;
return None&lt;br /&gt;
finally:&lt;br /&gt;
self._unlock()&lt;/p&gt;
&lt;p&gt;With this changes we would get all result sets and it's descriptions separately. For example, following code:&lt;/p&gt;
&lt;p&gt;import Sybase&lt;/p&gt;
&lt;p&gt;db=Sybase.connect('Sybase','user','pass','')&lt;br /&gt;
cur = db.cursor()&lt;br /&gt;
cur.execute('exec SampleProc')&lt;/p&gt;
&lt;p&gt;rs = cur.fetchall()&lt;br /&gt;
print rs&lt;br /&gt;
while cur.nextset():&lt;br /&gt;
rs = cur.fetchall()&lt;br /&gt;
print rs&lt;/p&gt;
&lt;p&gt;will return:&lt;/p&gt;
&lt;p&gt;[(1, 2), (11, 12)]&lt;br /&gt;
[(3, '4', 5), (31, '41', 51)]&lt;/p&gt;
&lt;p&gt;Also i've added property status_result to store processed return status. It can be accessed after fetching all of result sets.&lt;/p&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alexey Novikov</dc:creator><pubDate>Thu, 18 Feb 2010 12:19:25 -0000</pubDate><guid>https://sourceforge.net2484f0d6a1807a509b586681b136803daf60cebb</guid></item></channel></rss>