ODBCConnection.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. //
  2. // Copyright (c) 2008-2016 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "../../Precompiled.h"
  23. #include "../../Database/DatabaseEvents.h"
  24. #include "../../IO/Log.h"
  25. #ifdef _WIN32
  26. // Needs to be included above sql.h for windows
  27. #define NOMINMAX
  28. #include <windows.h>
  29. #endif
  30. #include <sqlext.h>
  31. namespace Atomic
  32. {
  33. DbConnection::DbConnection(Context* context, const String& connectionString) :
  34. Object(context),
  35. connectionString_(connectionString)
  36. {
  37. try
  38. {
  39. connectionImpl_ = nanodbc::connection(connectionString.CString());
  40. }
  41. catch (std::runtime_error& e)
  42. {
  43. HandleRuntimeError("Could not connect", e.what());
  44. }
  45. }
  46. DbConnection::~DbConnection()
  47. {
  48. try
  49. {
  50. Finalize();
  51. connectionImpl_.disconnect();
  52. }
  53. catch (std::runtime_error& e)
  54. {
  55. // This should not happen after finalizing the connection, log error in Release but assert in Debug
  56. HandleRuntimeError("Could not disconnect", e.what());
  57. assert(false);
  58. }
  59. }
  60. void DbConnection::Finalize()
  61. {
  62. // TODO
  63. }
  64. DbResult DbConnection::Execute(const String& sql, bool useCursorEvent)
  65. {
  66. DbResult result;
  67. try
  68. {
  69. result.resultImpl_ = nanodbc::execute(connectionImpl_, sql.Trimmed().CString());
  70. unsigned numCols = (unsigned)result.resultImpl_.columns();
  71. if (numCols)
  72. {
  73. result.columns_.Resize(numCols);
  74. for (unsigned i = 0; i < numCols; ++i)
  75. result.columns_[i] = result.resultImpl_.column_name((short)i).c_str();
  76. bool filtered = false;
  77. bool aborted = false;
  78. while (result.resultImpl_.next())
  79. {
  80. VariantVector colValues(numCols);
  81. for (unsigned i = 0; i < numCols; ++i)
  82. {
  83. if (!result.resultImpl_.is_null((short)i))
  84. {
  85. // We can only bind primitive data type that our Variant class supports
  86. switch (result.resultImpl_.column_c_datatype((short)i))
  87. {
  88. case SQL_C_LONG:
  89. colValues[i] = result.resultImpl_.get<int>((short)i);
  90. if (result.resultImpl_.column_datatype((short)i) == SQL_BIT)
  91. colValues[i] = colValues[i] != 0;
  92. break;
  93. case SQL_C_FLOAT:
  94. colValues[i] = result.resultImpl_.get<float>((short)i);
  95. break;
  96. case SQL_C_DOUBLE:
  97. colValues[i] = result.resultImpl_.get<double>((short)i);
  98. break;
  99. default:
  100. // All other types are stored using their string representation in the Variant
  101. colValues[i] = result.resultImpl_.get<nanodbc::string_type>((short)i).c_str();
  102. break;
  103. }
  104. }
  105. }
  106. if (useCursorEvent)
  107. {
  108. using namespace DbCursor;
  109. VariantMap& eventData = GetEventDataMap();
  110. eventData[P_DBCONNECTION] = this;
  111. eventData[P_RESULTIMPL] = &result.resultImpl_;
  112. eventData[P_SQL] = sql;
  113. eventData[P_NUMCOLS] = numCols;
  114. eventData[P_COLVALUES] = colValues;
  115. eventData[P_COLHEADERS] = result.columns_;
  116. eventData[P_FILTER] = false;
  117. eventData[P_ABORT] = false;
  118. SendEvent(E_DBCURSOR, eventData);
  119. filtered = eventData[P_FILTER].GetBool();
  120. aborted = eventData[P_ABORT].GetBool();
  121. }
  122. if (!filtered)
  123. result.rows_.Push(colValues);
  124. if (aborted)
  125. break;
  126. }
  127. }
  128. result.numAffectedRows_ = numCols ? -1 : result.resultImpl_.affected_rows();
  129. }
  130. catch (std::runtime_error& e)
  131. {
  132. HandleRuntimeError("Could not execute", e.what());
  133. }
  134. return result;
  135. }
  136. void DbConnection::HandleRuntimeError(const char* message, const char* cause)
  137. {
  138. StringVector tokens = (String(cause) + "::").Split(':'); // Added "::" as sentinels against unexpected cause format
  139. ATOMIC_LOGERRORF("%s: nanodbc:%s:%s", message, tokens[1].CString(), tokens[2].CString());
  140. }
  141. }