4#include "Stroika/Foundation/StroikaPreComp.h"
6#if qStroika_Foundation_Common_Platform_Windows
10#if qStroika_HasComponent_ODBC
17#include "Stroika/Foundation/Database/Exception.h"
24using namespace Stroika::Foundation::Database;
25using namespace Stroika::Foundation::Database::SQL::ODBC;
29using Memory::MakeSharedPtr;
31#if qStroika_HasComponent_ODBC
34 void ThrowIfSQLError_ (SQLRETURN r,
const String& message)
36 if ((r != SQL_SUCCESS) and (r != SQL_SUCCESS_WITH_INFO)) [[unlikely]] {
43 using Connection::Options;
44 struct Rep_ final : Stroika::Foundation::Database::SQL::ODBC::Connection::IRep {
45 SQLHDBC fConnectionHandle{
nullptr};
46 SQLHENV fODBCEnvironmentHandle{
nullptr};
48 Rep_ (
const Options& options)
51 if (not options.fDSN) {
52 Execution::Throw (
Exception{
"DSN Required"sv});
55 ThrowIfSQLError_ (::SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &fODBCEnvironmentHandle),
"Error AllocHandle"sv);
56 ThrowIfSQLError_ (::SQLSetEnvAttr (fODBCEnvironmentHandle, SQL_ATTR_ODBC_VERSION,
reinterpret_cast<void*
> (SQL_OV_ODBC3), 0),
58 ThrowIfSQLError_ (::SQLAllocHandle (SQL_HANDLE_DBC, fODBCEnvironmentHandle, &fConnectionHandle),
"Error AllocHDB"sv);
60 ::SQLSetConnectAttr (fConnectionHandle, SQL_LOGIN_TIMEOUT,
reinterpret_cast<SQLPOINTER*
> (5), 0);
65 SQLRETURN return_value =
66 ::SQLConnect (fConnectionHandle,
reinterpret_cast<SQLTCHAR*
> (
const_cast<TCHAR*
> (options.fDSN->AsSDKString ().c_str ())),
67 SQL_NTS,
nullptr, SQL_NTS,
nullptr, SQL_NTS);
68 if ((return_value != SQL_SUCCESS) && (return_value != SQL_SUCCESS_WITH_INFO)) {
73 SQLSMALLINT messageLength;
74 SQLTCHAR errorMessage[1024];
76 long errValue = ::SQLGetDiagRec (SQL_HANDLE_DBC, fConnectionHandle, 1, reinterpret_cast<SQLTCHAR*> (sqlState), &errorCode,
77 reinterpret_cast<SQLTCHAR*> (errorMessage),
std::size (errorMessage), &messageLength);
78 DISABLE_COMPILER_MSC_WARNING_END (4267)
79 if (errValue == SQL_SUCCESS) {
82 errorString += String::FromSDKString (
reinterpret_cast<TCHAR*
> (errorMessage));
84 else if (errValue == SQL_SUCCESS_WITH_INFO) {
85 errorString =
"Error message too long at"_k;
87 else if (errValue == SQL_ERROR) {
88 errorString +=
"RecNumber was negative or 0 or BufferLength was less than 0"_k;
90 else if (errValue == SQL_NO_DATA) {
91 errorString +=
"SQL no data"_k;
93 Execution::Throw (
Exception{errorString});
98 if (fConnectionHandle !=
nullptr) {
99 ::SQLFreeHandle (SQL_HANDLE_DBC, fConnectionHandle);
100 fConnectionHandle =
nullptr;
102 if (fODBCEnvironmentHandle !=
nullptr) {
103 ::SQLFreeHandle (SQL_HANDLE_ENV, fODBCEnvironmentHandle);
104 fODBCEnvironmentHandle =
nullptr;
110 if (fConnectionHandle !=
nullptr) {
111 ::SQLFreeHandle (SQL_HANDLE_DBC, fConnectionHandle);
113 if (fODBCEnvironmentHandle !=
nullptr) {
114 ::SQLFreeHandle (SQL_HANDLE_ENV, fODBCEnvironmentHandle);
117 virtual shared_ptr<const EngineProperties> GetEngineProperties ()
const override
121 virtual String GetEngineName ()
const override
125 virtual String GetSQL ([[maybe_unused]] NonStandardSQL n)
const override
131 virtual bool RequireStatementResetAfterModifyingStatmentToCompleteTransaction ()
const override
135 virtual bool SupportsNestedTransactions ()
const override
140 return MakeSharedPtr<const MyEngineProperties_> ();
152 virtual void Exec (
const String& )
override
164SQL::ODBC::Connection::Ptr::Ptr (
const shared_ptr<IRep>& src)
167#if qStroika_Foundation_Debug_AssertExternallySynchronizedMutex_Enabled
168 if (src !=
nullptr) {
169 fAssertExternallySynchronizedMutex.SetAssertExternallySynchronizedMutexContext (src->fAssertExternallySynchronizedMutex.GetSharedContext ());
179auto SQL::ODBC::Connection::New (
const Options& options) -> Ptr
181 return Ptr{MakeSharedPtr<Rep_> (options)};
189struct Statement::MyRep_ : IRep {
191 : fConnectionPtr_{db}
193#if USE_NOISY_TRACE_IN_THIS_MODULE_
198#if qStroika_Foundation_Debug_AssertExternallySynchronizedMutex_Enabled
199 _fAssertExternallySynchronizedMutex.SetAssertExternallySynchronizedMutexContext (
200 fConnectionPtr_.fAssertExternallySynchronizedMutex.GetSharedContext ());
202 u8string queryUTF8 = query.
AsUTF8 ();
210 virtual String GetSQL ([[maybe_unused]] WhichSQLFlag whichSQL)
const override
227 virtual void Bind ()
override
230 for (
auto i = fParameters_.begin (); i != fParameters_.end (); ++i) {
233 fParameters_.Update (i, p, &i);
237 virtual void Bind (
unsigned int parameterIndex,
const VariantValue& v)
override
240 fParameters_ (parameterIndex).fValue = v;
245 Require (not parameterName.empty ());
248 String pn = parameterName;
252 for (
unsigned int i = 0; i < fParameters_.length (); ++i) {
253 if (fParameters_[i].fName == pn) {
258 DbgTrace (
"Statement::Bind: Parameter '{}' not found in list {}"_f, parameterName,
259 fParameters_.Map<vector<String>> ([] (
const auto& i) { return i.fName; }));
262 virtual void Reset ()
override
264#if USE_NOISY_TRACE_IN_THIS_MODULE_
270 virtual optional<Row> GetNextRow ()
override
272#if USE_NOISY_TRACE_IN_THIS_MODULE_
281 vector<ColumnDescription> fColumns_;
286 : inherited{make_unique<MyRep_> (db, query)}
297 : fConnectionPtr_{db}
301 virtual void Commit ()
override
303 Require (not fCompleted_);
305 fConnectionPtr_->Exec (
"COMMIT;"sv);
309 Require (not fCompleted_);
311 fConnectionPtr_->Exec (
"ROLLBACK;"sv);
316 return fCompleted_ ? Disposition::eCompleted : Disposition::eNone;
319 bool fCompleted_{
false};
322 : inherited{make_unique<MyRep_> (db)}
#define AssertNotImplemented()
#define RequireNotReached()
#define RequireNotNull(p)
#define Stroika_Foundation_Debug_OptionalizeTraceArgs(...)
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
nonvirtual T AsUTF8() const
A generalization of a vector: a container whose elements are keyed by the natural numbers.
Simple variant-value (case variant union) object, with (variant) basic types analogous to a value in ...
EngineProperties captures the features associated with a given database engine (being talked to throu...
virtual void Rollback()=0
virtual Disposition GetDisposition() const =0
shared_lock< const AssertExternallySynchronizedMutex > ReadContext
Instantiate AssertExternallySynchronizedMutex::ReadContext to designate an area of code where protect...
unique_lock< AssertExternallySynchronizedMutex > WriteContext
Instantiate AssertExternallySynchronizedMutex::WriteContext to designate an area of code where protec...
DISABLE_COMPILER_MSC_WARNING_START(4996)