/* Copyright 2009 Virginia Polytechnic Institute and State University Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* This file contains the full implementation of the CBR class - the default * VTCROSS case-based reasoner. */ #ifndef CBR_H #define CBR_H #include #include #include #include #include #include #include "vtcross/common.h" #include "vtcross/debug.h" #include "vtcross/error.h" using namespace std; #define DATABASENAME "vtcross_cbr" /* This is an internal debugging function used by some sqlite3 function calls. * It is not used otherwise in the VTCROSS codebase. */ int32_t callback(void *notUsed, int argc, char **argv, char **azColName) { for(size_t i = 0; i < argc; i++) { LOG("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL"); } LOG("\n"); return 0; } /* The CBR class is designed to used as either as-is, or as a parent class. All * functions are declared virtual, and internal members are 'protected' rather * than private. If you require functionality in a CBR not specifically provided * by this class, include this header in your source file, create a new class * that derives from CBR, and implement your desired functionality over the * original virtual functions as necessary. */ class CBR { public: /* Constructors for the CBR class. Note that the default constructor * must be defined inline here so that super-calls from child classes * don't fail (i.e. we cannot rely on the compiler-provided constructor. */ CBR(){}; CBR(string _filename, string _tablename, string _cols[], uint32_t _len); CBR(string _filename, string _tablename, string _cols[], \ string _primcols[], uint32_t _len, uint32_t _primlen); /* Destructor for the CBR class. Note that this destructor will be * called automatically by any derived classes, and so child classes * should not repeat the freeing actions performed in this function. */ virtual ~CBR(); /* This function opens the VTCROSS database, or if it has not been * created yet, creates it. */ virtual int32_t OpenDatabase(); /* Execute a sqlite3 command and return the sqlite3 return code. */ virtual int32_t ExecuteCommand(); /* Execute a sqlite3 search command and store the results in the passed * retvals argument. */ virtual int32_t ExecuteSearchCommand(float *_retvals); /* Print the VTCROSS sqlite3 database. */ virtual void Print(); /* Search the VTCROSS database for specific fields and store the results * in the passed retvals argument. */ virtual int32_t Search(string _names[], int32_t *_ops, float *_vals, \ uint32_t _n, float *_retvals); virtual int32_t SearchSum(string _name, float *_retvals); virtual int32_t SearchRand(string _names[], int32_t *_ops, float *_vals, uint32_t _n, \ float *_retvals); /* Update a row in the VTCROSS sqlite3 database. */ virtual int32_t Update(string _where[], string _set[], float *_wherevals, \ float *_setvals, uint32_t _wherelen, uint32_t _setlen); /* Add a row to the VTCROSS sqlite3 database. */ virtual int32_t AddRow(string _cols[], float *_vals, uint32_t _len); protected: string filename; string tablename; string command; sqlite3 *db; uint32_t numColumns; }; CBR::CBR(string _filename, string _tablename, string _cols[], uint32_t _len) { /* Store database properties. */ filename = _filename; tablename = _tablename; numColumns = _len; /* Create the database (or open it if it already exists). */ OpenDatabase(); /* Generate the command that will create the initial table within the * VTCROSS database. */ command = "CREATE TABLE " + tablename + "("; for(size_t i = 0; i < numColumns; i++) { command += _cols[i] + " FLOAT"; /* If this column is not the last entry, add a comma to the command in * preperation for the next entry. */ if(i != numColumns - 1) command += ", "; } command += ");"; /* Execute the generated command. At this point, the database is ready for * use. */ ExecuteCommand(); } CBR::CBR(string _filename, string _tablename, string _cols[], \ string _primcols[], uint32_t _len, uint32_t _primlen) { /* Store database properties. */ filename = _filename; tablename = _tablename; numColumns = _len; /* Create the database (or open it if it already exists). */ OpenDatabase(); /* Generate the command that will create the initial table within the * VTCROSS database with primary keys. */ command = "CREATE TABLE " + tablename + "("; for(size_t i = 0; i < numColumns; i++) { command += _cols[i] + " FLOAT, "; } command += "PRIMARY KEY ("; for(size_t j = 0; j < _primlen; j++) { command += _primcols[j]; /* If this column is not the last entry, add a comma to the command in * preperation for the next entry. */ if(j != _primlen - 1) command += ", "; } command += "));"; /* Execute the generated command. At this point, the database is ready for * use. */ ExecuteCommand(); } CBR::~CBR() { /* Generate the sqlite command to delete a table and all of its contents, * and then execute it. */ command = "drop table " + tablename; ExecuteCommand(); /* Tell sqlite to clean up the database. */ command = "vacuum"; ExecuteCommand(); } int32_t CBR::OpenDatabase() { int32_t rc = sqlite3_open(filename.c_str(), &db); if(rc) { WARNING("Can't open database: %s\n", sqlite3_errmsg(db)); sqlite3_close(db); exit(1); } return rc; } int32_t CBR::ExecuteCommand() { char *zErrMsg = 0; int32_t rc = sqlite3_exec(db, command.c_str(), callback, 0, &zErrMsg); if(rc != SQLITE_OK) { WARNING("SQL error: %s: %s\n", zErrMsg, command.c_str()); sqlite3_free(zErrMsg); } return rc; } int32_t CBR::ExecuteSearchCommand(float *_retvals) { sqlite3_stmt *pStatement; int32_t rc = sqlite3_prepare_v2(db, command.c_str(), -1, &pStatement, NULL); if(rc == SQLITE_OK) { if(sqlite3_step(pStatement) == SQLITE_ROW) { for(size_t i = 0; i < numColumns; ++i) { _retvals[i] = sqlite3_column_double(pStatement, i); } } else { LOG("CBR:: No matched results returning default.\n"); rc = 31337; } } else { WARNING("CBR:: Error executing SQL statement. rc = %i\n%s\n", rc, command.c_str()); } sqlite3_finalize(pStatement); return rc; } void CBR::Print() { /* Generate the sqlite command to print the database, which is effectively a * 'select all elements' command, and then execute it. */ command = "select " + tablename + ".* from " + tablename + ";"; ExecuteCommand(); LOG("database %s, table %s:\n", filename.c_str(), tablename.c_str()); } int32_t CBR::Search(string _names[], int32_t *_ops, float *_vals, uint32_t _n, \ float *_retvals) { char str_buffer[64]; const string ops_str[] = {"==", "!=", ">", ">=", "<", "<="}; command = "select " + tablename + ".* from " + tablename + " where "; for(size_t i = 0; i < _n; i++) { /* Make sure that the passed ops value is valid. */ if((_ops[i] < 0) || (_ops[i] > 5)) { ERROR(1, "Error: cbr_search(), invalid ops id : %d\n", _ops[i]); } command += _names[i] + ops_str[_ops[i]]; sprintf(str_buffer, "%E", _vals[i]); command += string(str_buffer); if(i < _n - 1) command += " AND "; else command += " order by utility desc;"; } //LOG("CBR::Search - command: %s\n", command.c_str()); return ExecuteSearchCommand(_retvals); } int32_t CBR::SearchSum(string _name, float *_retvals) { command = "select SUM(" + tablename + "." + _name + ") from " + tablename + ";"; return ExecuteSearchCommand(_retvals); } int32_t CBR::SearchRand(string _names[], int32_t *_ops, float *_vals, uint32_t _n, \ float *_retvals) { char str_buffer[64]; const char *ops_str[] = {"==", "!=", ">", ">=", "<", "<="}; command = "select " + tablename + ".* from " + tablename + " where "; for(size_t i = 0; i < _n; i++) { /* Make sure that the passed ops value is valid. */ if((_ops[i] < 0) || (_ops[i] > 5)) { ERROR(1, "Error: cbr_search(), invalid ops id : %d\n", _ops[i]); } command += _names[i] + ops_str[_ops[i]]; sprintf(str_buffer, "%E", _vals[i]); command += str_buffer; if(i < _n - 1) command += " AND "; else command += " order by RAND();"; } return ExecuteSearchCommand(_retvals); } int32_t CBR::Update(string _where[], string _set[], float *_wherevals, float *_setvals, uint32_t _wherelen, uint32_t _setlen) { char str_buffer[64]; /* Generate the command to update the table. */ command = "UPDATE " + tablename + " SET "; for(size_t i = 0; i < _setlen; i++) { command += _set[i] + " = "; sprintf(str_buffer, "%f", _setvals[i]); command += string(str_buffer) + " "; if(i != _setlen - 1) command += ", "; } command += " WHERE "; for(size_t j = 0; j < _wherelen; j++) { command += _where[j] + " = "; sprintf(str_buffer, "%f", _wherevals[j]); command += string(str_buffer) + " "; if(j != _wherelen - 1) command += "AND "; } command += ";"; return ExecuteCommand(); } int32_t CBR::AddRow(string _cols[], float *_vals, uint32_t _len) { char str_buffer[64]; command = "insert into " + tablename + " ("; for(size_t i = 0; i < _len; i++) { command += _cols[i]; if(i != numColumns - 1) command += ", "; } command += ") values("; for(size_t j = 0; j < _len; j++) { // TODO I have no idea what the below question is about. // ???? how to fill the values if numColumns != _len // assume = in the following sprintf(str_buffer, "%f", _vals[j]); command += str_buffer; if(j != numColumns - 1) command += ", "; } command += ");"; return ExecuteCommand(); } #endif