@@ -45,11 +45,15 @@ ConnectionHandles::~ConnectionHandles() {
4545}
4646
4747void ConnectionHandles::clear () {
48- SQL_LOG_DEBUG_STREAM (" ConnectionHandles::clear - size = " << _statementHandles.size ());
48+ std::lock_guard<std::mutex> lock (_handlesMutex);
49+
50+ SQL_LOG_DEBUG_STREAM (" ConnectionHandles::clear - ENTER - thread="
51+ << std::this_thread::get_id () << " size=" << _statementHandles.size ());
4952
5053 // Direct iteration - no need for intermediate vector
5154 for (const auto & [id, safeHandle] : _statementHandles) {
52- SQL_LOG_DEBUG_STREAM (" destruct OdbcStatementCache - free statement " << id);
55+ SQL_LOG_DEBUG_STREAM (" ConnectionHandles::clear - processing statement " << id
56+ << " thread=" << std::this_thread::get_id ());
5357 if (safeHandle) {
5458 auto handle = safeHandle->get ();
5559 if (handle) {
@@ -73,40 +77,55 @@ void ConnectionHandles::clear() {
7377 // Also try to explicitly close the cursor/statement
7478 SQLCloseCursor (handle->get_handle ());
7579 SQLFreeStmt (handle->get_handle (), SQL_CLOSE);
80+ } else {
81+ SQL_LOG_WARNING_STREAM (" ConnectionHandles::clear - statement " << id
82+ << " handle already invalid/freed" );
7683 }
7784 // During connection cleanup, we need to force-free handles
7885 // Reset references first to avoid warning about freeing referenced handles
86+ SQL_LOG_DEBUG_STREAM (" ConnectionHandles::clear - freeing statement " << id
87+ << " thread=" << std::this_thread::get_id ());
7988 safeHandle->resetReferences ();
8089 safeHandle->free ();
90+ } else {
91+ SQL_LOG_WARNING_STREAM (" ConnectionHandles::clear - statement " << id
92+ << " safeHandle is null" );
8193 }
8294 }
8395
8496 _statementHandles.clear ();
97+ SQL_LOG_DEBUG_STREAM (" ConnectionHandles::clear - EXIT - thread=" << std::this_thread::get_id ());
8598}
8699
87- shared_ptr<IOdbcStatementHandle> ConnectionHandles::find (const long statement_id) {
100+ shared_ptr<IOdbcStatementHandle> ConnectionHandles::find_unlocked (const long statement_id) {
101+ // NOTE: This is an internal method - caller must hold _handlesMutex
88102 const auto itr = _statementHandles.find (statement_id);
89103 if (itr != _statementHandles.end ()) {
90104 auto ref = itr->second ->get ();
91105 if (ref) {
92106 return ref;
93107 }
94- SQL_LOG_WARNING_STREAM (" Found invalid statement handle for id: " << statement_id);
108+ SQL_LOG_WARNING_STREAM (" find_unlocked - statement " << statement_id
109+ << " found but handle is invalid" );
95110 }
96111 return nullptr ;
97112}
98113
99114size_t ConnectionHandles::size () const {
115+ std::lock_guard<std::mutex> lock (_handlesMutex);
100116 return _statementHandles.size ();
101117}
102118
103119bool ConnectionHandles::exists (long statement_id) const {
120+ std::lock_guard<std::mutex> lock (_handlesMutex);
104121 return _statementHandles.find (statement_id) != _statementHandles.end ();
105122}
106123
107124shared_ptr<IOdbcStatementHandle> ConnectionHandles::store (const long statement_id,
108125 shared_ptr<IOdbcStatementHandle> handle) {
109- SQL_LOG_DEBUG_STREAM (" ConnectionHandles::store - statementId = " << statement_id);
126+ // NOTE: This is an internal method - caller must hold _handlesMutex
127+ SQL_LOG_DEBUG_STREAM (" ConnectionHandles::store - statementId=" << statement_id
128+ << " thread=" << std::this_thread::get_id ());
110129
111130 auto safeHandle = std::make_shared<SafeHandle<IOdbcStatementHandle>>(
112131 " Statement_" + std::to_string (statement_id), handle);
@@ -115,23 +134,33 @@ shared_ptr<IOdbcStatementHandle> ConnectionHandles::store(const long statement_i
115134 const auto [it, inserted] = _statementHandles.try_emplace (statement_id, safeHandle);
116135
117136 if (!inserted) {
118- SQL_LOG_ERROR_STREAM (
119- " ConnectionHandles::store - statementId already exists = " << statement_id );
137+ SQL_LOG_ERROR_STREAM (" ConnectionHandles::store - statementId already exists = " << statement_id
138+ << " thread= " << std::this_thread::get_id () );
120139 return nullptr ;
121140 }
122141
142+ SQL_LOG_DEBUG_STREAM (" ConnectionHandles::store - statementId=" << statement_id
143+ << " stored successfully, total handles=" << _statementHandles.size ());
123144 return handle;
124145}
125146
126147shared_ptr<IOdbcStatementHandle> ConnectionHandles::checkout (long statement_id) {
127148 if (statement_id < 0 ) {
128- SQL_LOG_ERROR_STREAM (" ConnectionHandles::checkout - invalid statementId = " << statement_id);
149+ SQL_LOG_ERROR_STREAM (" ConnectionHandles::checkout - invalid statementId=" << statement_id
150+ << " thread=" << std::this_thread::get_id ());
129151 return nullptr ;
130152 }
131153
132- auto statement = find (statement_id);
154+ std::lock_guard<std::mutex> lock (_handlesMutex);
155+
156+ SQL_LOG_DEBUG_STREAM (" ConnectionHandles::checkout - ENTER statementId=" << statement_id
157+ << " thread=" << std::this_thread::get_id ()
158+ << " current_handles=" << _statementHandles.size ());
159+
160+ auto statement = find_unlocked (statement_id);
133161 if (statement) {
134- SQL_LOG_DEBUG_STREAM (" ConnectionHandles::checkout ok on statementId = " << statement_id);
162+ SQL_LOG_DEBUG_STREAM (" ConnectionHandles::checkout - found existing statementId=" << statement_id
163+ << " thread=" << std::this_thread::get_id ());
135164 return statement;
136165 }
137166
@@ -143,38 +172,54 @@ shared_ptr<IOdbcStatementHandle> ConnectionHandles::checkout(long statement_id)
143172 // Get connection handle for allocation
144173 auto connRef = connectionHandle_->get ();
145174 if (!connRef) {
146- SQL_LOG_ERROR_STREAM (" ConnectionHandles::checkout - connection handle invalid" );
175+ SQL_LOG_ERROR_STREAM (" ConnectionHandles::checkout - connection handle invalid, statementId="
176+ << statement_id << " thread=" << std::this_thread::get_id ());
147177 return nullptr ;
148178 }
149179
150180 if (!safeHandle->alloc (connRef->get_handle ())) {
151- SQL_LOG_ERROR_STREAM (" ConnectionHandles::checkout - failed to allocate statement handle" );
181+ SQL_LOG_ERROR_STREAM (" ConnectionHandles::checkout - failed to allocate statement handle for statementId="
182+ << statement_id << " thread=" << std::this_thread::get_id ());
152183 return nullptr ;
153184 }
154185
155186 // Store the SafeHandle wrapper
156187 _statementHandles[statement_id] = safeHandle;
157188
158- SQL_LOG_DEBUG_STREAM (
159- " ConnectionHandles::checkout - created new handle for statementId = " << statement_id);
189+ SQL_LOG_DEBUG_STREAM (" ConnectionHandles::checkout - created new handle for statementId="
190+ << statement_id << " thread=" << std::this_thread::get_id ()
191+ << " total_handles=" << _statementHandles.size ());
160192
161193 // Return the handle itself, not the SafeHandle
162194 return safeHandle->get ();
163195}
164196
165197void ConnectionHandles::checkin (long statementId) {
166- SQL_LOG_DEBUG_STREAM (" ConnectionHandles::checkin - statementId = " << statementId);
198+ std::lock_guard<std::mutex> lock (_handlesMutex);
199+
200+ SQL_LOG_DEBUG_STREAM (" ConnectionHandles::checkin - ENTER statementId=" << statementId
201+ << " thread=" << std::this_thread::get_id ()
202+ << " current_handles=" << _statementHandles.size ());
167203
168204 const auto itr = _statementHandles.find (statementId);
169205 if (itr == _statementHandles.end ()) {
170- SQL_LOG_ERROR_STREAM (
171- " ConnectionHandles::checkin - no handle found for statementId = " << statementId);
206+ // This can happen legitimately if clear() already freed this statement
207+ // during connection close - not necessarily an error
208+ SQL_LOG_WARNING_STREAM (" ConnectionHandles::checkin - statementId=" << statementId
209+ << " not found (may have been freed by clear()) thread="
210+ << std::this_thread::get_id ());
172211 return ;
173212 }
174213
175214 // Free the handle through SafeHandle
215+ SQL_LOG_DEBUG_STREAM (" ConnectionHandles::checkin - freeing statementId=" << statementId
216+ << " thread=" << std::this_thread::get_id ());
176217 itr->second ->free ();
177218 _statementHandles.erase (statementId);
219+
220+ SQL_LOG_DEBUG_STREAM (" ConnectionHandles::checkin - EXIT statementId=" << statementId
221+ << " thread=" << std::this_thread::get_id ()
222+ << " remaining_handles=" << _statementHandles.size ());
178223}
179224
180225// Return the interface pointer
0 commit comments