Browse Source

[2342] getVersion() is now working

This change adds the code for prepared statement creation and the
template for retrieving information from the database.
Stephen Morris 12 years ago
parent
commit
c7cb0c3336

+ 7 - 0
src/lib/dhcp/lease_mgr.h

@@ -68,6 +68,13 @@ public:
         isc::Exception(file, line, what) {}
 };
 
+/// @brief Exception thrown on failure to execute a database function
+class DbOperationError : public Exception {
+public:
+    DbOperationError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
 /// @brief specifies unique subnet identifier
 /// @todo: Move this to subnet.h once ticket #2237 is merged
 typedef uint32_t SubnetID;

+ 101 - 7
src/lib/dhcp/mysql_lease_mgr.cc

@@ -76,32 +76,88 @@ MySqlLeaseMgr::openDatabase() {
     }
 }
 
+void
+MySqlLeaseMgr::prepareStatement(StatementIndex index, const char* text) {
+    // Validate that there is space for the statement in the statements array
+    // and that nothing has been placed there before.
+    if ((index >= statements_.size()) || (statements_[index] != NULL)) {
+        isc_throw(InvalidParameter, "invalid prepared statement index or "
+                  "statement index not null");
+    }
+
+    // All OK, so prepare the statement
+    raw_statements_[index] = std::string(text);
+
+    statements_[index] = mysql_stmt_init(mysql_);
+    if (statements_[index] == NULL) {
+        isc_throw(DbOperationError, "unable to allocate MySQL prepared "
+                  "statement structure" << mysql_error(mysql_));
+    }
+
+    int status = mysql_stmt_prepare(statements_[index], text, strlen(text));
+    if (status != 0) {
+        isc_throw(DbOperationError, "unable to prepare MySQL statement <" <<
+                  text << ">, reason: " << mysql_error(mysql_));
+    }
+}
+
+void
+MySqlLeaseMgr::prepareStatements() {
+    // Allocate space for all statements
+    statements_.clear();
+    statements_.resize(NUM_STATEMENTS, NULL);
+    
+    raw_statements_.clear();
+    raw_statements_.resize(NUM_STATEMENTS, std::string(""));
+
+    // Now allocate the statements
+    prepareStatement(SELECT_VERSION,
+                     "SELECT version, minor FROM schema_version");
+}
+
 
 MySqlLeaseMgr::MySqlLeaseMgr(const LeaseMgr::ParameterMap& parameters) 
-    : LeaseMgr(parameters), mysql_(NULL), major_(0), minor_(0) {
+    : LeaseMgr(parameters), mysql_(NULL) {
 
     // Allocate context for MySQL - it is destroyed in the destructor.
     mysql_ = mysql_init(NULL);
-    std::cerr << "cerr: mysql_ is " << long(mysql_) << std::endl;
-    std::cout << "cout: mysql_ is " << long(mysql_) << std::endl;
 
     // Open the database
     openDatabase();
-    
+
+    // Disable autocommit
+    my_bool result = mysql_autocommit(mysql_, 0);
+    if (result != 0) {
+        isc_throw(DbOperationError, mysql_error(mysql_));
+    }
+
+    // Prepare all statements likely to be used.
+    prepareStatements();
 }
 
 MySqlLeaseMgr::~MySqlLeaseMgr() {
+    // Free up the prepared statements, ignoring errors. (What would we do
+    // about them - we're destroying this object and are not really concerned
+    // with errors on a database connection that it about to go away.)
+    for (int i = 0; i < statements_.size(); ++i) {
+        if (statements_[i] != NULL) {
+            (void) mysql_stmt_close(statements_[i]);
+            statements_[i] = NULL;
+        }
+    }
+
+    // Close the database
     mysql_close(mysql_);
     mysql_ = NULL;
 }
 
 bool
-MySqlLeaseMgr::addLease(isc::dhcp::Lease4Ptr /* lease */) {
+MySqlLeaseMgr::addLease(Lease4Ptr /* lease */) {
     return (false);
 }
 
 bool
-MySqlLeaseMgr::addLease(isc::dhcp::Lease6Ptr /* lease */) {
+MySqlLeaseMgr::addLease(Lease6Ptr /* lease */) {
     return (false);
 }
 
@@ -184,7 +240,45 @@ MySqlLeaseMgr::getDescription() const {
 
 std::pair<uint32_t, uint32_t>
 MySqlLeaseMgr::getVersion() const {
-    return (std::make_pair(major_, minor_));
+    uint32_t    major;      // Major version number
+    uint32_t    minor;      // Minor version number
+
+    // Execute the prepared statement
+    int status = mysql_stmt_execute(statements_[SELECT_VERSION]);
+    if (status != 0) {
+        isc_throw(DbOperationError, "unable to execute <"
+                  << raw_statements_[SELECT_VERSION] << "> - reason: " <<
+                  mysql_error(mysql_));
+    }
+
+    // Bind the output of the statement to the appropriate variables.
+    MYSQL_BIND bind[2];
+    memset(bind, 0, sizeof(bind));
+
+    bind[0].buffer_type = MYSQL_TYPE_LONG;
+    bind[0].is_unsigned = 1;
+    bind[0].buffer = &major;
+    bind[0].buffer_length = sizeof(major);
+
+    bind[1].buffer_type = MYSQL_TYPE_LONG;
+    bind[1].is_unsigned = 1;
+    bind[1].buffer = &minor;
+    bind[1].buffer_length = sizeof(minor);
+
+    status = mysql_stmt_bind_result(statements_[SELECT_VERSION], bind);
+    if (status != 0) {
+        isc_throw(DbOperationError, "unable to bind result set: " <<
+                  mysql_error(mysql_));
+    }
+
+    // Get the result
+    status = mysql_stmt_fetch(statements_[SELECT_VERSION]);
+    if (status != 0) {
+        isc_throw(DbOperationError, "unable to obtain result set: " <<
+                  mysql_error(mysql_));
+    }
+
+    return (std::make_pair(major, minor));
 }
 
 }; // end of isc::dhcp namespace

+ 32 - 3
src/lib/dhcp/mysql_lease_mgr.h

@@ -225,6 +225,35 @@ public:
     virtual std::pair<uint32_t, uint32_t> getVersion() const;
 
 private:
+    /// @brief Enum of Statements
+    ///
+    /// This is provided to set indexes into a list of prepared statements.
+    enum StatementIndex {
+        SELECT_VERSION,                 // Obtain version number
+        NUM_STATEMENTS                  // Number of statements
+    };
+
+    /// @brief Prepare Single Statement
+    ///
+    /// Creates a prepared statement from the text given and adds it to the
+    /// statements_ vector at the given index.
+    ///
+    /// @param index Index into the statements_ vector into which the text
+    ///        should be placed.  The vector must be big enough for the index
+    ///        to be valid, else an exception will be thrown.
+    /// @param text Text of the SQL statement to be prepared.
+    ///
+    /// @exception DbOperationError MySQL operation failed, exception will give
+    ///            text indicating the reason.
+    /// @exception InvalidParameter 'index' is not valid for the vector.
+    void prepareStatement(StatementIndex index, const char* text);
+
+    /// @brief Prepare statements
+    ///
+    /// Creates the prepared statements for all of the SQL statements used
+    /// by the MySQL backend.
+    void prepareStatements();
+
     /// @brief Open Database
     ///
     /// Opens the database using the information supplied in the parameters
@@ -234,9 +263,9 @@ private:
     void openDatabase();
 
     // Members
-    MYSQL*      mysql_;     ///< MySQL context object
-    uint32_t    major_;     ///< Major version number
-    uint32_t    minor_;     ///< Minor version number
+    MYSQL*              mysql_;                 ///< MySQL context object
+    std::vector<std::string> raw_statements_;   ///< Raw text of statements
+    std::vector<MYSQL_STMT*> statements_;       ///< Prepared statements
 };
 
 }; // end of isc::dhcp namespace

+ 9 - 2
src/lib/dhcp/tests/mysql_lease_mgr_unittest.cc

@@ -95,12 +95,19 @@ TEST_F(MySqlLeaseMgrTest, OpenDatabase) {
     ASSERT_NO_THROW(lmptr = LeaseMgrFactory::create(connectionString(
         VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)));
     ASSERT_TRUE(lmptr);
+}
+
+TEST_F(MySqlLeaseMgrTest, CheckVersion) {
+    // Open database
+    LeaseMgrPtr lmptr;
+    ASSERT_NO_THROW(lmptr = LeaseMgrFactory::create(connectionString(
+        VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)));
+    ASSERT_TRUE(lmptr);
 
-/*
+    // Check version
     pair<uint32_t, uint32_t> version = lmptr->getVersion();
     EXPECT_EQ(0, version.first);
     EXPECT_EQ(1, version.second);
-*/
 }
 
 }; // end of anonymous namespace