mysql_connection.cc 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. // Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <dhcpsrv/dhcpsrv_log.h>
  15. #include <dhcpsrv/mysql_connection.h>
  16. #include <exceptions/exceptions.h>
  17. #include <boost/foreach.hpp>
  18. #include <boost/algorithm/string.hpp>
  19. #include <algorithm>
  20. #include <iostream>
  21. #include <iterator>
  22. #include <map>
  23. #include <sstream>
  24. #include <string>
  25. #include <time.h>
  26. using namespace isc;
  27. using namespace isc::dhcp;
  28. using namespace std;
  29. namespace isc {
  30. namespace dhcp {
  31. /// @brief Maximum size of database fields
  32. ///
  33. /// The following constants define buffer sizes for variable length database
  34. /// fields. The values should be greater than or equal to the length set in
  35. /// the schema definition.
  36. ///
  37. /// The exception is the length of any VARCHAR fields: buffers for these should
  38. /// be set greater than or equal to the length of the field plus 1: this allows
  39. /// for the insertion of a trailing null whatever data is returned.
  40. const my_bool MLM_FALSE = 0; ///< False value
  41. const my_bool MLM_TRUE = 1; ///< True value
  42. ///@}
  43. // Open the database using the parameters passed to the constructor.
  44. void
  45. MySqlConnection::openDatabase() {
  46. // Set up the values of the parameters
  47. const char* host = "localhost";
  48. string shost;
  49. try {
  50. shost = getParameter("host");
  51. host = shost.c_str();
  52. } catch (...) {
  53. // No host. Fine, we'll use "localhost"
  54. }
  55. const char* user = NULL;
  56. string suser;
  57. try {
  58. suser = getParameter("user");
  59. user = suser.c_str();
  60. } catch (...) {
  61. // No user. Fine, we'll use NULL
  62. }
  63. const char* password = NULL;
  64. string spassword;
  65. try {
  66. spassword = getParameter("password");
  67. password = spassword.c_str();
  68. } catch (...) {
  69. // No password. Fine, we'll use NULL
  70. }
  71. const char* name = NULL;
  72. string sname;
  73. try {
  74. sname = getParameter("name");
  75. name = sname.c_str();
  76. } catch (...) {
  77. // No database name. Throw a "NoName" exception
  78. isc_throw(NoDatabaseName, "must specified a name for the database");
  79. }
  80. // Set options for the connection:
  81. //
  82. // Automatic reconnection: after a period of inactivity, the client will
  83. // disconnect from the database. This option causes it to automatically
  84. // reconnect when another operation is about to be done.
  85. my_bool auto_reconnect = MLM_TRUE;
  86. int result = mysql_options(mysql_, MYSQL_OPT_RECONNECT, &auto_reconnect);
  87. if (result != 0) {
  88. isc_throw(DbOpenError, "unable to set auto-reconnect option: " <<
  89. mysql_error(mysql_));
  90. }
  91. // Set SQL mode options for the connection: SQL mode governs how what
  92. // constitutes insertable data for a given column, and how to handle
  93. // invalid data. We want to ensure we get the strictest behavior and
  94. // to reject invalid data with an error.
  95. const char *sql_mode = "SET SESSION sql_mode ='STRICT_ALL_TABLES'";
  96. result = mysql_options(mysql_, MYSQL_INIT_COMMAND, sql_mode);
  97. if (result != 0) {
  98. isc_throw(DbOpenError, "unable to set SQL mode options: " <<
  99. mysql_error(mysql_));
  100. }
  101. // Open the database.
  102. //
  103. // The option CLIENT_FOUND_ROWS is specified so that in an UPDATE,
  104. // the affected rows are the number of rows found that match the
  105. // WHERE clause of the SQL statement, not the rows changed. The reason
  106. // here is that MySQL apparently does not update a row if data has not
  107. // changed and so the "affected rows" (retrievable from MySQL) is zero.
  108. // This makes it hard to distinguish whether the UPDATE changed no rows
  109. // because no row matching the WHERE clause was found, or because a
  110. // row was found but no data was altered.
  111. MYSQL* status = mysql_real_connect(mysql_, host, user, password, name,
  112. 0, NULL, CLIENT_FOUND_ROWS);
  113. if (status != mysql_) {
  114. isc_throw(DbOpenError, mysql_error(mysql_));
  115. }
  116. }
  117. // Prepared statement setup. The textual form of an SQL statement is stored
  118. // in a vector of strings (text_statements_) and is used in the output of
  119. // error messages. The SQL statement is also compiled into a "prepared
  120. // statement" (stored in statements_), which avoids the overhead of compilation
  121. // during use. As prepared statements have resources allocated to them, the
  122. // class destructor explicitly destroys them.
  123. void
  124. MySqlConnection::prepareStatement(uint32_t index, const char* text) {
  125. // Validate that there is space for the statement in the statements array
  126. // and that nothing has been placed there before.
  127. if ((index >= statements_.size()) || (statements_[index] != NULL)) {
  128. isc_throw(InvalidParameter, "invalid prepared statement index (" <<
  129. static_cast<int>(index) << ") or indexed prepared " <<
  130. "statement is not null");
  131. }
  132. // All OK, so prepare the statement
  133. text_statements_[index] = std::string(text);
  134. statements_[index] = mysql_stmt_init(mysql_);
  135. if (statements_[index] == NULL) {
  136. isc_throw(DbOperationError, "unable to allocate MySQL prepared "
  137. "statement structure, reason: " << mysql_error(mysql_));
  138. }
  139. int status = mysql_stmt_prepare(statements_[index], text, strlen(text));
  140. if (status != 0) {
  141. isc_throw(DbOperationError, "unable to prepare MySQL statement <" <<
  142. text << ">, reason: " << mysql_error(mysql_));
  143. }
  144. }
  145. void
  146. MySqlConnection::prepareStatements(const TaggedStatement tagged_statements[],
  147. size_t num_statements) {
  148. // Allocate space for all statements
  149. statements_.clear();
  150. statements_.resize(num_statements, NULL);
  151. text_statements_.clear();
  152. text_statements_.resize(num_statements, std::string(""));
  153. // Created the MySQL prepared statements for each DML statement.
  154. for (int i = 0; tagged_statements[i].text != NULL; ++i) {
  155. prepareStatement(tagged_statements[i].index,
  156. tagged_statements[i].text);
  157. }
  158. }
  159. } // namespace isc::dhcp
  160. } // namespace isc