mysql_connection.cc 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  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 <algorithm>
  18. #include <iostream>
  19. #include <iterator>
  20. #include <stdint.h>
  21. #include <string>
  22. using namespace isc;
  23. using namespace isc::dhcp;
  24. using namespace std;
  25. namespace isc {
  26. namespace dhcp {
  27. /// @brief Maximum size of database fields
  28. ///
  29. /// The following constants define buffer sizes for variable length database
  30. /// fields. The values should be greater than or equal to the length set in
  31. /// the schema definition.
  32. ///
  33. /// The exception is the length of any VARCHAR fields: buffers for these should
  34. /// be set greater than or equal to the length of the field plus 1: this allows
  35. /// for the insertion of a trailing null whatever data is returned.
  36. const my_bool MLM_FALSE = 0; ///< False value
  37. const my_bool MLM_TRUE = 1; ///< True value
  38. ///@}
  39. // Open the database using the parameters passed to the constructor.
  40. void
  41. MySqlConnection::openDatabase() {
  42. // Set up the values of the parameters
  43. const char* host = "localhost";
  44. string shost;
  45. try {
  46. shost = getParameter("host");
  47. host = shost.c_str();
  48. } catch (...) {
  49. // No host. Fine, we'll use "localhost"
  50. }
  51. const char* user = NULL;
  52. string suser;
  53. try {
  54. suser = getParameter("user");
  55. user = suser.c_str();
  56. } catch (...) {
  57. // No user. Fine, we'll use NULL
  58. }
  59. const char* password = NULL;
  60. string spassword;
  61. try {
  62. spassword = getParameter("password");
  63. password = spassword.c_str();
  64. } catch (...) {
  65. // No password. Fine, we'll use NULL
  66. }
  67. const char* name = NULL;
  68. string sname;
  69. try {
  70. sname = getParameter("name");
  71. name = sname.c_str();
  72. } catch (...) {
  73. // No database name. Throw a "NoName" exception
  74. isc_throw(NoDatabaseName, "must specified a name for the database");
  75. }
  76. // Set options for the connection:
  77. //
  78. // Automatic reconnection: after a period of inactivity, the client will
  79. // disconnect from the database. This option causes it to automatically
  80. // reconnect when another operation is about to be done.
  81. my_bool auto_reconnect = MLM_TRUE;
  82. int result = mysql_options(mysql_, MYSQL_OPT_RECONNECT, &auto_reconnect);
  83. if (result != 0) {
  84. isc_throw(DbOpenError, "unable to set auto-reconnect option: " <<
  85. mysql_error(mysql_));
  86. }
  87. // Set SQL mode options for the connection: SQL mode governs how what
  88. // constitutes insertable data for a given column, and how to handle
  89. // invalid data. We want to ensure we get the strictest behavior and
  90. // to reject invalid data with an error.
  91. const char *sql_mode = "SET SESSION sql_mode ='STRICT_ALL_TABLES'";
  92. result = mysql_options(mysql_, MYSQL_INIT_COMMAND, sql_mode);
  93. if (result != 0) {
  94. isc_throw(DbOpenError, "unable to set SQL mode options: " <<
  95. mysql_error(mysql_));
  96. }
  97. // Open the database.
  98. //
  99. // The option CLIENT_FOUND_ROWS is specified so that in an UPDATE,
  100. // the affected rows are the number of rows found that match the
  101. // WHERE clause of the SQL statement, not the rows changed. The reason
  102. // here is that MySQL apparently does not update a row if data has not
  103. // changed and so the "affected rows" (retrievable from MySQL) is zero.
  104. // This makes it hard to distinguish whether the UPDATE changed no rows
  105. // because no row matching the WHERE clause was found, or because a
  106. // row was found but no data was altered.
  107. MYSQL* status = mysql_real_connect(mysql_, host, user, password, name,
  108. 0, NULL, CLIENT_FOUND_ROWS);
  109. if (status != mysql_) {
  110. isc_throw(DbOpenError, mysql_error(mysql_));
  111. }
  112. }
  113. // Prepared statement setup. The textual form of an SQL statement is stored
  114. // in a vector of strings (text_statements_) and is used in the output of
  115. // error messages. The SQL statement is also compiled into a "prepared
  116. // statement" (stored in statements_), which avoids the overhead of compilation
  117. // during use. As prepared statements have resources allocated to them, the
  118. // class destructor explicitly destroys them.
  119. void
  120. MySqlConnection::prepareStatement(uint32_t index, const char* text) {
  121. // Validate that there is space for the statement in the statements array
  122. // and that nothing has been placed there before.
  123. if ((index >= statements_.size()) || (statements_[index] != NULL)) {
  124. isc_throw(InvalidParameter, "invalid prepared statement index (" <<
  125. static_cast<int>(index) << ") or indexed prepared " <<
  126. "statement is not null");
  127. }
  128. // All OK, so prepare the statement
  129. text_statements_[index] = std::string(text);
  130. statements_[index] = mysql_stmt_init(mysql_);
  131. if (statements_[index] == NULL) {
  132. isc_throw(DbOperationError, "unable to allocate MySQL prepared "
  133. "statement structure, reason: " << mysql_error(mysql_));
  134. }
  135. int status = mysql_stmt_prepare(statements_[index], text, strlen(text));
  136. if (status != 0) {
  137. isc_throw(DbOperationError, "unable to prepare MySQL statement <" <<
  138. text << ">, reason: " << mysql_error(mysql_));
  139. }
  140. }
  141. void
  142. MySqlConnection::prepareStatements(const TaggedStatement tagged_statements[],
  143. size_t num_statements) {
  144. // Allocate space for all statements
  145. statements_.clear();
  146. statements_.resize(num_statements, NULL);
  147. text_statements_.clear();
  148. text_statements_.resize(num_statements, std::string(""));
  149. // Created the MySQL prepared statements for each DML statement.
  150. for (int i = 0; tagged_statements[i].text != NULL; ++i) {
  151. prepareStatement(tagged_statements[i].index,
  152. tagged_statements[i].text);
  153. }
  154. }
  155. /// @brief Destructor
  156. MySqlConnection::~MySqlConnection() {
  157. // Free up the prepared statements, ignoring errors. (What would we do
  158. // about them? We're destroying this object and are not really concerned
  159. // with errors on a database connection that is about to go away.)
  160. for (int i = 0; i < statements_.size(); ++i) {
  161. if (statements_[i] != NULL) {
  162. (void) mysql_stmt_close(statements_[i]);
  163. statements_[i] = NULL;
  164. }
  165. }
  166. statements_.clear();
  167. text_statements_.clear();
  168. }
  169. } // namespace isc::dhcp
  170. } // namespace isc