leaked ODBC statement handles
Terrence Enger
tenger at iseries-guru.com
Wed Jul 18 06:55:21 PDT 2012
I have let myself write quite a bit in-line. Meanwhile, here are the
actual questions arising.
(*) Do you agree that ODatabaseMetaDataResultSet ctor should warn if
it does not get a statement handle?
(*) Is there any value to a bug report at this stage?
Thanks,
Terry.
On Wed, 2012-07-18 at 05:15 +0200, Lionel Elie Mamane wrote:
> On Tue, Jul 17, 2012 at 03:46:14PM -0400, Terrence Enger wrote:
> > On Tue, 2012-07-17 at 19:08 +0200, Lionel Elie Mamane wrote:
> >> On Mon, Jul 16, 2012 at 10:15:02PM -0400, Terrence Enger wrote:
>
> >>> I am chasing some "leaked" ODBC statement handles.
>
> >>> I see that ODatabaseMetaDataResultSet.cxx takes care *not* to free
a
> >>> statement handle which has not been subjected to one of 13 member
> >>> functions with names starting "open...". Questions arising ...
>
> >> That is a bug. For the history, look at commit
> >> aa3f42832756b7165a0722b2d013a572acf224c8
> >>
http://cgit.freedesktop.org/libreoffice/core/commit/?id=aa3f42832756b7165a0722b2d013a572acf224c8
>
> >> (...) I can easily believe the code was leaking statement handles
> >> in this way even back then (or
>
> > Well, I can demonstrate at least a leak. Removing m_bCloseHandle
will
> > of course fix the leak. Still, I wonder if the leak could be a sign
> > of a bug is client code somewhere. Thoughts?
>
> I'm not sure what you mean there.
Something created a ODatabaseMetaDataResultSet. Presumably the client
code had some intended purpose for the object. But the statement
handle appeared in the ODBC log file only in SQLAllocHandle. I
conclude that the client code did not do very much; as the client code
did not even free the handle, there is no sign that the client made a
deliberate decision to abandon its purpose. Hmm, that sounds like it
could be a bug, or an opportunity for optimization, or an opportunity
for simplification.
I, too, am not sure exactly what I mean: the backtraces from
construction of the otherwise-unused ODatabaseMetaDataResultSet
objects each show scores of call levels. Even assuming that my
hypothesized failed purpose is closer to the ctor than to main, I am
daunted.
>
> >> double-freeing them or whatever).
>
> > Well, yes. I can imagine (but have not yet tried to demonstrate)
this
> > happening:
>
> > (*) Client code calls close, which calls dispose, which frees the
> > statement handle
>
> > (*) Client code calls dispose, which frees the statement handle a
> > second time. I have not looked at the Reference class since the
> > days of OO.o, but IIRC it calls dispose of the referenced class.
>
> > (*) The destructor calls dispose, which frees the statement handle a
> > third time..
>
> Good point. However, calling disposing() multiple times does not lead
> to double or triple freeing of the statement handle with the current
> code.
Oh my. I was wrong in so many ways ...
>
> ODatabaseMetaDataResultSet::disposing:
>
> ::osl::MutexGuard aGuard(m_aMutex);
> if(m_bFreeHandle)
> m_pConnection->freeStatementHandle(m_aStatementHandle);
>
>
> So, m_pConnection->freeStatementHandle can be called multiple times,
> OK. But look at OConnection::freeStatementHandle:
>
> void OConnection::freeStatementHandle(SQLHANDLE& _pHandle)
> {
> N3SQLFreeStmt(_pHandle,SQL_RESET_PARAMS);
> N3SQLFreeStmt(_pHandle,SQL_UNBIND);
> N3SQLFreeStmt(_pHandle,SQL_CLOSE);
> N3SQLFreeHandle(SQL_HANDLE_STMT,_pHandle);
>
> _pHandle = SQL_NULL_HANDLE;
> }
>
> 1) The argument is passed by reference
>
> 2) After freeing the handle, it sets the handle to a special dummy
> value; because it is passed by reference, it is updated in the
> caller, and DatabaseMetaDataResultSet::m_aStatementHandle is set to
> the dummy value.
>
> 3) So the second time disposing() is called, SQLFreeHandle is called
> on the dummy handle, not on the original "real" handle.
>
Exactly so. And moreover ...
(*) I confused ODatabaseMetaDataResultSet::dispose, which the dtor
calls, with ODatabaseMetaDataResultSet::disposing, which frees the
statement handle. Sheesh, it's not as if the names differed in
only one letter, or something.
(*) It was manifest in gdb that the dtor does not call disposing. I
was too close-minded to notice.
(*) The ODBC log files do not show any null handles.
Altogether, a bad day of work. Sorry.
> The remaining doubt I could have is whether calling free on the NULL
> handle is guaranteed safe; I don't see anything to that extent in the
> ODBC docs, maybe it would be safer to add:
>
> if (_pHandle == SQL_NULL_HANDLE)
> return;
>
> to the top of OConnection::freeStatementHandle?
>
Yes, I shall do that. It is more drastic what I was preparing to
offer in this function.
>
> > Removing m_bCloseHandle from ODatabaseMetaDataResultSet clears away
> > many questions that I was accumulating, and it makes somewhat
> > plausible my guess that the protocol for using
> > ODatabaseMetaDataResultSet is something like this:
>
> > construct
> > exactly one open<whatever>
> > in any order
> > | one or more get<whatever>
> > | zero or more calls repositioning the result set
> > | zero or more calls querying position of result set
> > exactly one close
> > exactly one dispose
> > destruct
>
> All these "exactly one" are too dangerous; as far as possible, the
> class should be safe for "zero or more" of anything :)
>
> Except for things like "exactly one construct() before anything else",
> which are pretty much guaranteed by C++ semantics :)
>
> > If the guess is plausible, should the class complain (SAL_WARN_IF, I
> > presume) about violations of the protocol?
>
> If there are legitimate protocol limitations, it is good, at the very
> least in debug mode, to check for protocol violations and react
> accordingly; from a warning to an exception, as appropriate.
>
> My first guess is that there are no such legitimate limitations here,
> but you've looked at this code more than me; if you think there are,
> let me know.
>
>
The removal of m_bCloseHandle makes it much easier to understand the
code, so I no longer feel the need to invent elaborate hypotheses.
Add your skepticism to this, and I think the question is dead.
(I may hack the class to warn of an object essentially unused
throughout its lifetime, but that is a bit of ad-hocery that should
not be inflicted on anybody else.)
I am making one more gratuitous change: ODatabaseMetaDataResultSet
ctor warns if it does not get a statment handle. The justification is
... well, I guess it's just my nast, suspicious mind. May I have your
blessing?
Thank you for your patience with me.
Terry.
More information about the LibreOffice
mailing list