|
From: Skip M. <sk...@po...> - 2004-09-04 11:45:19
|
If a cursor's execute() method results in an error (say, from a trigger
failing for some reason) it appears there is no recourse to continue other
than closing the connection and starting over. Here's the simple test case:
c = Sybase.Connection(...)
c1 = c.cursor()
c1.execute("select * from blah")
c2 = c.cursor()
c2.execute("select 1")
The first execute() call results in a DatabaseError exception because there
is no column "blah". The second execute() call also fails with a
DatabaseError, this time in ct_send. The message is:
ct_send(): user api layer: external error: This routine cannot be called
because another command structure has results pending.
How do I toss those pending results? I've tried wrapping the c1 calls in
c.begin()/c.rollback() calls. I've tried closing and/or deleting c1. I've
tried fetching the nonexistent results of the failed execute() call.
Nothing seems to make Sybase happy again until a new connection is created.
Is there a better (less sledgehammer-like) way to recover from errors?
Thanks,
--
Skip Montanaro
Got spam? http://www.spambayes.org/
sk...@po...
|
|
From: Josh C. <na...@gm...> - 2004-09-04 12:04:55
|
I'd like to know this too, I've run into the same problems.
-Josh
On Fri, 3 Sep 2004 13:45:07 -0500, Skip Montanaro <sk...@po...> wrote:
>
> If a cursor's execute() method results in an error (say, from a trigger
> failing for some reason) it appears there is no recourse to continue other
> than closing the connection and starting over. Here's the simple test case:
>
> c = Sybase.Connection(...)
> c1 = c.cursor()
> c1.execute("select * from blah")
> c2 = c.cursor()
> c2.execute("select 1")
>
> The first execute() call results in a DatabaseError exception because there
> is no column "blah". The second execute() call also fails with a
> DatabaseError, this time in ct_send. The message is:
>
> ct_send(): user api layer: external error: This routine cannot be called
> because another command structure has results pending.
>
> How do I toss those pending results? I've tried wrapping the c1 calls in
> c.begin()/c.rollback() calls. I've tried closing and/or deleting c1. I've
> tried fetching the nonexistent results of the failed execute() call.
> Nothing seems to make Sybase happy again until a new connection is created.
>
> Is there a better (less sledgehammer-like) way to recover from errors?
>
> Thanks,
>
> --
> Skip Montanaro
> Got spam? http://www.spambayes.org/
> sk...@po...
> _______________________________________________
> Python-sybase mailing list
> Pyt...@ww...
> https://www.object-craft.com.au/cgi-bin/mailman/listinfo/python-sybase
>
|
|
From: Alex S. <ale...@gm...> - 2004-09-10 08:47:15
|
On Fri, 3 Sep 2004 14:04:50 -0500, Josh Close <na...@gm...> wrote: > I'd like to know this too, I've run into the same problems. Me too. I've been doing the same thing as Skip, not really satisfying. cheers, Alex -- Alex Suzuki | as...@cy... | http://n.ethz.ch/student/asuzuki/ "Computer Science is no more about computers than astronomy is about telescopes." - E.W.Dijkstra |
|
From: Dave C. <dj...@ob...> - 2004-11-16 18:37:53
|
Skip Montanaro wrote:
> If a cursor's execute() method results in an error (say, from a trigger
> failing for some reason) it appears there is no recourse to continue other
> than closing the connection and starting over. Here's the simple test case:
>
> c = Sybase.Connection(...)
> c1 = c.cursor()
> c1.execute("select * from blah")
> c2 = c.cursor()
> c2.execute("select 1")
>
> The first execute() call results in a DatabaseError exception because there
> is no column "blah". The second execute() call also fails with a
> DatabaseError, this time in ct_send. The message is:
>
> ct_send(): user api layer: external error: This routine cannot be called
> because another command structure has results pending.
>
> How do I toss those pending results? I've tried wrapping the c1 calls in
> c.begin()/c.rollback() calls. I've tried closing and/or deleting c1. I've
> tried fetching the nonexistent results of the failed execute() call.
> Nothing seems to make Sybase happy again until a new connection is created.
I think I know what is causing this. If someone could do an experiment
for me I will make the change and cut a new release (with the output
hook patch as well).
The cursor class uses a _FetchLazy object to perform lazy fetching of
rows from the server. The _FetchLazy object has an internal state which
tracks the current state of the connection being used to fetch results.
It is constructed in the IDLE state. Looking at the Cursor.execute()
method:
# Discard any previous results
self._fetcher = None
# Prepare to retrieve new results.
fetcher = self._fetcher = _FetchLazy(self._owner)
cmd = fetcher._cmd
cmd.ct_command(CS_LANG_CMD, sql)
for name, value in params.items():
buf = DataBuf(value)
buf.name = name
status = cmd.ct_param(buf)
if status != CS_SUCCEED:
fetcher._raise_error(Error, 'ct_param')
self.description = fetcher.start(self.arraysize)
If an exception is raised during either the ct_command() function or
ct_send() the fetcher is still in the IDLE state. This means that when
the next execute() is performed and the previous fetcher is discarded,
no cancel action is performed.
There are two possible ways to attempt a fix.
1) Construct the Fetcher in the FETCHING state. This will mean that any
exception raised in the ct_command() or the ct_send() function will
result in ct_cancel(CS_CANCEL_ALL) when the Fetcher is deleted.
In _FetchLazy.__init__() change:
self._state = _LAZY_IDLE
to
self._state = _LAZY_FETCHING
2) Set the Fetcher state to FETCHING immediately before calling
ct_send() in _FetchLazy.start().
In _FetchLazy.start() change:
status = self._cmd.ct_send()
to
self._set_state(_LAZY_FETCHING)
status = self._cmd.ct_send()
For various reasons I do not currently have a working Sybase server at
the moment so would appreciate it if someone else could make the change
and test for me.
- Dave
--
http://www.object-craft.com.au
|
|
From: Skip M. <sk...@po...> - 2005-01-07 19:23:46
|
Sorry to take so long to get back to this problem. Squeaky wheels and all,
you know...
>> If a cursor's execute() method results in an error (say, from a
>> trigger failing for some reason) it appears there is no recourse to
>> continue other than closing the connection and starting over. Here's
>> the simple test case:
>>
>> c = Sybase.Connection(...)
>> c1 = c.cursor()
>> c1.execute("select * from blah")
>> c2 = c.cursor()
>> c2.execute("select 1")
...
Dave> I think I know what is causing this. If someone could do an
Dave> experiment for me I will make the change and cut a new release
Dave> (with the output hook patch as well).
...
Dave> There are two possible ways to attempt a fix.
Dave> 1) Construct the Fetcher in the FETCHING state. This will mean
Dave> that any exception raised in the ct_command() or the ct_send()
Dave> function will result in ct_cancel(CS_CANCEL_ALL) when the
Dave> Fetcher is deleted.
Dave> In _FetchLazy.__init__() change:
Dave> self._state = _LAZY_IDLE
Dave> to
Dave> self._state = _LAZY_FETCHING
This didn't work.
Dave> 2) Set the Fetcher state to FETCHING immediately before calling
Dave> ct_send() in _FetchLazy.start().
Dave> In _FetchLazy.start() change:
Dave> status = self._cmd.ct_send()
Dave> to
Dave> self._set_state(_LAZY_FETCHING)
Dave> status = self._cmd.ct_send()
This did...
Thanks,
Skip
|
|
From: Skip M. <sk...@po...> - 2005-01-07 19:23:46
|
Perhaps I spoke a bit too soon...
Dave> There are two possible ways to attempt a fix.
Dave> 1) Construct the Fetcher in the FETCHING state. This will mean
Dave> that any exception raised in the ct_command() or the ct_send()
Dave> function will result in ct_cancel(CS_CANCEL_ALL) when the
Dave> Fetcher is deleted.
Dave> In _FetchLazy.__init__() change:
Dave> self._state = _LAZY_IDLE
Dave> to
Dave> self._state = _LAZY_FETCHING
Skip> This didn't work.
Dave> 2) Set the Fetcher state to FETCHING immediately before calling
Dave> ct_send() in _FetchLazy.start().
Dave> In _FetchLazy.start() change:
Dave> status = self._cmd.ct_send()
Dave> to
Dave> self._set_state(_LAZY_FETCHING)
Dave> status = self._cmd.ct_send()
Skip> This did...
Solution #2 worked on Solaris/Intel, but not on Linux (neither did solution
#1). Cursors created after an error still give a ct_send() error with this
message when their execute() method is called:
ct_send(): user api layer: external error: This routine cannot be called
because another command structure has results pending.
Skip
|
|
From: Dave C. <dj...@ob...> - 2005-01-11 18:10:22
|
Skip Montanaro wrote: > Perhaps I spoke a bit too soon... > > Dave> There are two possible ways to attempt a fix. > > Dave> 1) Construct the Fetcher in the FETCHING state. This will mean > Dave> that any exception raised in the ct_command() or the ct_send() > Dave> function will result in ct_cancel(CS_CANCEL_ALL) when the > Dave> Fetcher is deleted. > > Dave> In _FetchLazy.__init__() change: > Dave> self._state = _LAZY_IDLE > Dave> to > Dave> self._state = _LAZY_FETCHING > > Skip> This didn't work. > > Dave> 2) Set the Fetcher state to FETCHING immediately before calling > Dave> ct_send() in _FetchLazy.start(). > > Dave> In _FetchLazy.start() change: > Dave> status = self._cmd.ct_send() > Dave> to > Dave> self._set_state(_LAZY_FETCHING) > Dave> status = self._cmd.ct_send() > > Skip> This did... > > Solution #2 worked on Solaris/Intel, but not on Linux (neither did solution > #1). Cursors created after an error still give a ct_send() error with this > message when their execute() method is called: > > ct_send(): user api layer: external error: This routine cannot be called > because another command structure has results pending. Hmmm... Looks like I have to do some more research into the CT library state machine. - Dave -- http://www.object-craft.com.au |