ODBCConnection.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. // Copyright (c) 2008-2023 the Urho3D project
  2. // License: MIT
  3. #include "../../Precompiled.h"
  4. #include "../../Database/DatabaseEvents.h"
  5. #include "../../IO/Log.h"
  6. #ifdef _WIN32
  7. // Needs to be included above sql.h for windows
  8. #define NOMINMAX
  9. #include "../../Engine/WinWrapped.h"
  10. #endif
  11. #include <sqlext.h>
  12. namespace Urho3D
  13. {
  14. DbConnection::DbConnection(Context* context, const String& connectionString) :
  15. Object(context),
  16. connectionString_(connectionString)
  17. {
  18. try
  19. {
  20. connectionImpl_ = nanodbc::connection(connectionString.CString());
  21. }
  22. catch (std::runtime_error& e)
  23. {
  24. HandleRuntimeError("Could not connect", e.what());
  25. }
  26. }
  27. DbConnection::~DbConnection()
  28. {
  29. try
  30. {
  31. Finalize();
  32. connectionImpl_.disconnect();
  33. }
  34. catch (std::runtime_error& e)
  35. {
  36. // This should not happen after finalizing the connection, log error in Release but assert in Debug
  37. HandleRuntimeError("Could not disconnect", e.what());
  38. assert(false);
  39. }
  40. }
  41. void DbConnection::Finalize()
  42. {
  43. // TODO
  44. }
  45. DbResult DbConnection::Execute(const String& sql, bool useCursorEvent)
  46. {
  47. DbResult result;
  48. try
  49. {
  50. result.resultImpl_ = nanodbc::execute(connectionImpl_, sql.Trimmed().CString());
  51. unsigned numCols = (unsigned)result.resultImpl_.columns();
  52. if (numCols)
  53. {
  54. result.columns_.Resize(numCols);
  55. for (unsigned i = 0; i < numCols; ++i)
  56. result.columns_[i] = result.resultImpl_.column_name((short)i).c_str();
  57. bool filtered = false;
  58. bool aborted = false;
  59. while (result.resultImpl_.next())
  60. {
  61. VariantVector colValues(numCols);
  62. for (unsigned i = 0; i < numCols; ++i)
  63. {
  64. if (!result.resultImpl_.is_null((short)i))
  65. {
  66. // We can only bind primitive data type that our Variant class supports
  67. switch (result.resultImpl_.column_c_datatype((short)i))
  68. {
  69. case SQL_C_LONG:
  70. colValues[i] = result.resultImpl_.get<int>((short)i);
  71. if (result.resultImpl_.column_datatype((short)i) == SQL_BIT)
  72. colValues[i] = colValues[i] != 0;
  73. break;
  74. case SQL_C_FLOAT:
  75. colValues[i] = result.resultImpl_.get<float>((short)i);
  76. break;
  77. case SQL_C_DOUBLE:
  78. colValues[i] = result.resultImpl_.get<double>((short)i);
  79. break;
  80. default:
  81. // All other types are stored using their string representation in the Variant
  82. colValues[i] = result.resultImpl_.get<nanodbc::string>((short)i).c_str();
  83. break;
  84. }
  85. }
  86. }
  87. if (useCursorEvent)
  88. {
  89. using namespace DbCursor;
  90. VariantMap& eventData = GetEventDataMap();
  91. eventData[P_DBCONNECTION] = this;
  92. eventData[P_RESULTIMPL] = &result.resultImpl_;
  93. eventData[P_SQL] = sql;
  94. eventData[P_NUMCOLS] = numCols;
  95. eventData[P_COLVALUES] = colValues;
  96. eventData[P_COLHEADERS] = result.columns_;
  97. eventData[P_FILTER] = false;
  98. eventData[P_ABORT] = false;
  99. SendEvent(E_DBCURSOR, eventData);
  100. filtered = eventData[P_FILTER].GetBool();
  101. aborted = eventData[P_ABORT].GetBool();
  102. }
  103. if (!filtered)
  104. result.rows_.Push(colValues);
  105. if (aborted)
  106. break;
  107. }
  108. }
  109. result.numAffectedRows_ = numCols ? -1 : result.resultImpl_.affected_rows();
  110. }
  111. catch (std::runtime_error& e)
  112. {
  113. HandleRuntimeError("Could not execute", e.what());
  114. }
  115. return result;
  116. }
  117. void DbConnection::HandleRuntimeError(const char* message, const char* cause)
  118. {
  119. StringVector tokens = (String(cause) + "::").Split(':'); // Added "::" as sentinels against unexpected cause format
  120. URHO3D_LOGERRORF("%s: nanodbc:%s:%s", message, tokens[1].CString(), tokens[2].CString());
  121. }
  122. }