-
Notifications
You must be signed in to change notification settings - Fork 48
Expand file tree
/
Copy pathconnection.h
More file actions
120 lines (95 loc) · 4.16 KB
/
connection.h
File metadata and controls
120 lines (95 loc) · 4.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include "../ddbc_bindings.h"
#include <memory>
#include <mutex>
#include <string>
// Represents a single ODBC database connection.
// Manages connection handles.
// Note: This class does NOT implement pooling logic directly.
//
// THREADING MODEL (per DB-API 2.0 threadsafety=1):
// - Connections should NOT be shared between threads in normal usage
// - However, _childStatementHandles is mutex-protected because:
// 1. Python GC/finalizers can run from any thread
// 2. Native code may release GIL during blocking ODBC calls
// 3. Provides safety if user accidentally shares connection
// - All accesses to _childStatementHandles are guarded by _childHandlesMutex
class Connection {
public:
Connection(const std::wstring& connStr, bool fromPool);
~Connection();
// Establish the connection using the stored connection string.
void connect(const py::dict& attrs_before = py::dict());
// Disconnect and free the connection handle.
// Throws on SQLDisconnect failure (use for user-facing close()).
void disconnect();
// Disconnect without throwing — safe for destructors, pool cleanup, and reset() failure paths.
void disconnect_nothrow() noexcept;
// Commit the current transaction.
void commit();
// Rollback the current transaction.
void rollback();
// Enable or disable autocommit mode.
void setAutocommit(bool value);
// Check whether autocommit is enabled.
bool getAutocommit() const;
bool isAlive() const;
bool reset();
void updateLastUsed();
std::chrono::steady_clock::time_point lastUsed() const;
// Allocate a new statement handle on this connection.
SqlHandlePtr allocStatementHandle();
// Get information about the driver and data source
py::object getInfo(SQLUSMALLINT infoType) const;
SQLRETURN setAttribute(SQLINTEGER attribute, py::object value);
// Add getter for DBC handle for error reporting
const SqlHandlePtr& getDbcHandle() const { return _dbcHandle; }
private:
void allocateDbcHandle();
void checkError(SQLRETURN ret) const;
void applyAttrsBefore(const py::dict& attrs_before);
// Shared disconnect logic: marks child handles, calls SQLDisconnect, frees handle.
// Returns the SQLRETURN from SQLDisconnect (or SQL_SUCCESS if no handle).
SQLRETURN disconnect_impl() noexcept;
std::wstring _connStr;
bool _fromPool = false;
bool _autocommit = true;
SqlHandlePtr _dbcHandle;
std::chrono::steady_clock::time_point _lastUsed;
std::wstring wstrStringBuffer; // wstr buffer for string attribute setting
std::string strBytesBuffer; // string buffer for byte attributes setting
// Track child statement handles to mark them as implicitly freed when connection closes
// Uses weak_ptr to avoid circular references and allow normal cleanup
// THREAD-SAFETY: All accesses must be guarded by _childHandlesMutex
std::vector<std::weak_ptr<SqlHandle>> _childStatementHandles;
// Counter for periodic compaction of expired weak_ptrs
// Compact every N allocations to avoid O(n²) overhead in hot path
// THREAD-SAFETY: Protected by _childHandlesMutex
size_t _allocationsSinceCompaction = 0;
static constexpr size_t COMPACTION_INTERVAL = 100;
// Mutex protecting _childStatementHandles and _allocationsSinceCompaction
// Prevents data races between allocStatementHandle() and disconnect(),
// or concurrent GC finalizers running from different threads
mutable std::mutex _childHandlesMutex;
};
class ConnectionHandle {
public:
ConnectionHandle(const std::string& connStr, bool usePool,
const py::dict& attrsBefore = py::dict());
~ConnectionHandle();
void close();
void commit();
void rollback();
void setAutocommit(bool enabled);
bool getAutocommit() const;
SqlHandlePtr allocStatementHandle();
void setAttr(int attribute, py::object value);
// Get information about the driver and data source
py::object getInfo(SQLUSMALLINT infoType) const;
private:
std::shared_ptr<Connection> _conn;
bool _usePool;
std::wstring _connStr;
};