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"
23using namespace Stroika::Foundation::Database;
24using namespace Stroika::Foundation::Database::SQL::ODBC;
29#if qStroika_HasComponent_ODBC
32 void ThrowIfSQLError_ (SQLRETURN r,
const String& message)
34 if ((r != SQL_SUCCESS) and (r != SQL_SUCCESS_WITH_INFO)) [[unlikely]] {
41 using Connection::Options;
42 struct Rep_ final : Stroika::Foundation::Database::SQL::ODBC::Connection::IRep {
43 SQLHDBC fConnectionHandle{
nullptr};
44 SQLHENV fODBCEnvironmentHandle{
nullptr};
46 Rep_ (
const Options& options)
49 if (not options.fDSN) {
53 ThrowIfSQLError_ (::SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &fODBCEnvironmentHandle),
"Error AllocHandle"sv);
54 ThrowIfSQLError_ (::SQLSetEnvAttr (fODBCEnvironmentHandle, SQL_ATTR_ODBC_VERSION,
reinterpret_cast<void*
> (SQL_OV_ODBC3), 0),
56 ThrowIfSQLError_ (::SQLAllocHandle (SQL_HANDLE_DBC, fODBCEnvironmentHandle, &fConnectionHandle),
"Error AllocHDB"sv);
58 ::SQLSetConnectAttr (fConnectionHandle, SQL_LOGIN_TIMEOUT,
reinterpret_cast<SQLPOINTER*
> (5), 0);
63 SQLRETURN return_value =
64 ::SQLConnect (fConnectionHandle,
reinterpret_cast<SQLTCHAR*
> (
const_cast<TCHAR*
> (options.fDSN->AsSDKString ().c_str ())),
65 SQL_NTS,
nullptr, SQL_NTS,
nullptr, SQL_NTS);
66 if ((return_value != SQL_SUCCESS) && (return_value != SQL_SUCCESS_WITH_INFO)) {
71 SQLSMALLINT messageLength;
72 SQLTCHAR errorMessage[1024];
74 long errValue = ::SQLGetDiagRec (SQL_HANDLE_DBC, fConnectionHandle, 1, reinterpret_cast<SQLTCHAR*> (sqlState), &errorCode,
75 reinterpret_cast<SQLTCHAR*> (errorMessage), Memory::NEltsOf (errorMessage), &messageLength);
76 DISABLE_COMPILER_MSC_WARNING_END (4267)
77 if (errValue == SQL_SUCCESS) {
80 errorString += String::FromSDKString (
reinterpret_cast<TCHAR*
> (errorMessage));
82 else if (errValue == SQL_SUCCESS_WITH_INFO) {
83 errorString =
"Error message too long at"_k;
85 else if (errValue == SQL_ERROR) {
86 errorString +=
"RecNumber was negative or 0 or BufferLength was less than 0"_k;
88 else if (errValue == SQL_NO_DATA) {
89 errorString +=
"SQL no data"_k;
96 if (fConnectionHandle !=
nullptr) {
97 ::SQLFreeHandle (SQL_HANDLE_DBC, fConnectionHandle);
98 fConnectionHandle =
nullptr;
100 if (fODBCEnvironmentHandle !=
nullptr) {
101 ::SQLFreeHandle (SQL_HANDLE_ENV, fODBCEnvironmentHandle);
102 fODBCEnvironmentHandle =
nullptr;
108 if (fConnectionHandle !=
nullptr) {
109 ::SQLFreeHandle (SQL_HANDLE_DBC, fConnectionHandle);
111 if (fODBCEnvironmentHandle !=
nullptr) {
112 ::SQLFreeHandle (SQL_HANDLE_ENV, fODBCEnvironmentHandle);
115 virtual shared_ptr<const EngineProperties> GetEngineProperties ()
const override
119 virtual String GetEngineName ()
const override
123 virtual String GetSQL ([[maybe_unused]] NonStandardSQL n)
const override
129 virtual bool RequireStatementResetAfterModifyingStatmentToCompleteTransaction ()
const override
133 virtual bool SupportsNestedTransactions ()
const override
138 return make_shared<const MyEngineProperties_> ();
150 virtual void Exec (
const String& )
override
162SQL::ODBC::Connection::Ptr::Ptr (
const shared_ptr<IRep>& src)
165#if qStroika_Foundation_Debug_AssertExternallySynchronizedMutex_Enabled
166 if (src !=
nullptr) {
167 fAssertExternallySynchronizedMutex.SetAssertExternallySynchronizedMutexContext (src->fAssertExternallySynchronizedMutex.GetSharedContext ());
177auto SQL::ODBC::Connection::New (
const Options& options) -> Ptr
179 return Ptr{make_shared<Rep_> (options)};
187struct Statement::MyRep_ : IRep {
189 : fConnectionPtr_{db}
191#if USE_NOISY_TRACE_IN_THIS_MODULE_
196#if qStroika_Foundation_Debug_AssertExternallySynchronizedMutex_Enabled
197 _fAssertExternallySynchronizedMutex.SetAssertExternallySynchronizedMutexContext (
198 fConnectionPtr_.fAssertExternallySynchronizedMutex.GetSharedContext ());
200 u8string queryUTF8 = query.
AsUTF8 ();
208 virtual String GetSQL ([[maybe_unused]] WhichSQLFlag whichSQL)
const override
225 virtual void Bind ()
override
228 for (
auto i = fParameters_.begin (); i != fParameters_.end (); ++i) {
231 fParameters_.Update (i, p, &i);
235 virtual void Bind (
unsigned int parameterIndex,
const VariantValue& v)
override
238 fParameters_ (parameterIndex).fValue = v;
243 Require (not parameterName.empty ());
246 String pn = parameterName;
250 for (
unsigned int i = 0; i < fParameters_.length (); ++i) {
251 if (fParameters_[i].fName == pn) {
256 DbgTrace (
"Statement::Bind: Parameter '{}' not found in list {}"_f, parameterName,
257 fParameters_.Map<vector<String>> ([] (
const auto& i) { return i.fName; }));
260 virtual void Reset ()
override
262#if USE_NOISY_TRACE_IN_THIS_MODULE_
268 virtual optional<Row> GetNextRow ()
override
270#if USE_NOISY_TRACE_IN_THIS_MODULE_
279 vector<ColumnDescription> fColumns_;
284 : inherited{make_unique<MyRep_> (db, query)}
295 : fConnectionPtr_{db}
299 virtual void Commit ()
override
301 Require (not fCompleted_);
303 fConnectionPtr_->Exec (
"COMMIT;"sv);
307 Require (not fCompleted_);
309 fConnectionPtr_->Exec (
"ROLLBACK;"sv);
314 return fCompleted_ ? Disposition::eCompleted : Disposition::eNone;
317 bool fCompleted_{
false};
320 : 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)
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...