#include #include #include "libdb.h" #include "MirrorDatabase.h" MirrorDatabase::MirrorDatabase (const char *server, const char* database) throw(const char*) { _remote_result = NULL; _remote_SQL = mysql_init (NULL); if (_remote_SQL == NULL) { _error_msg = "MirrorDatabase::MirrorDatabase(): mysql_init - Out of memory?\n"; std::cerr << _error_msg << std::endl; throw _error_msg.c_str(); } // Connect to the database server on this machine unsigned int timeout_val = 60; mysql_options (_remote_SQL, MYSQL_OPT_CONNECT_TIMEOUT, (char *) &timeout_val); if (mysql_real_connect (_remote_SQL, server, "nobody", "", database, 0, NULL, 0) == NULL) { _error_msg = "MirrorDatabase::MirrorDatabase(): mysql_real_connect - "; _error_msg = _error_msg + mysql_error (_remote_SQL) + "\n"; mysql_close(_remote_SQL); _remote_SQL = NULL; std::cerr << _error_msg << std::endl; throw _error_msg.c_str(); } } MirrorDatabase::~MirrorDatabase() { if(_remote_SQL != NULL){ mysql_close (_remote_SQL); } if(_remote_result != NULL) { mysql_free_result(_remote_result); _remote_result = NULL; } } void MirrorDatabase::BeginTransaction () { #ifdef ALL_DB if(dbIsSQLite()) { #endif #if defined (SQLITE_DB) || defined (ALL_DB) int rc = dbQueryExec("BEGIN TRANSACTION"); if (rc == 0) std::cerr << "MirrorDatabase::BeginTransaction - begin transaction error: " << dbErrorMsg() << std::endl; #endif #ifdef ALL_DB } #endif } void MirrorDatabase::EndTransaction () { #ifdef ALL_DB if(dbIsSQLite()) { int rc = dbQueryExec("COMMIT TRANSACTION"); if (rc == 0) std::cerr << "MirrorDatabase::EndTransaction - end transaction error: " << dbErrorMsg() << std::endl; } #endif #ifdef SQLITE_DB int rc = dbQueryExec("COMMIT TRANSACTION"); if (rc == 0) std::cerr << "MirrorDatabase::EndTransaction - end transaction error: " << dbErrorMsg() << std::endl; #endif } void MirrorDatabase::EndRemoteQuery () { if(_remote_result != NULL) { mysql_free_result(_remote_result); _remote_result = NULL; } } void MirrorDatabase::SetTables (const char* remote_table, const char* local_table) { if (strcmp (local_table, "") == 0) { local_table = remote_table; } _local_table = local_table; _remote_table = remote_table; } void MirrorDatabase::LocalAddTable (const char* remote_table, const char* local_table_name) { if(_remote_SQL == NULL) { _error_msg = "MirrorDatabase::LocalAddTable() - Database not connected\n"; throw _error_msg.c_str(); } MYSQL_RES *result; SetTables (remote_table, local_table_name); // Check if table exists remotely std::string query = "SELECT * FROM "; query += _remote_table; query += " LIMIT 1"; if (mysql_query (_remote_SQL, query.c_str()) != 0) { _error_msg = "MirrorDatabase::LocalAddTable() - mysql_query: "; _error_msg = _error_msg + mysql_error (_remote_SQL) + "\n"; throw _error_msg.c_str(); } else { if ((result = mysql_store_result(_remote_SQL)) == NULL) { _error_msg = "MirrorDatabase::LocalAddTable() - mysql_store_result: %s\n"; _error_msg = _error_msg + mysql_error (_remote_SQL) + "\n"; mysql_free_result(result); throw _error_msg.c_str(); } } mysql_free_result(result); // Exists Remotely, now check if it exists locally if(!LocalTableExists(_local_table.c_str())) { // get how it was created std::string show_query = "SHOW CREATE TABLE "; show_query += _remote_table; MYSQL_ROW row; #if defined(MYSQL_DB) || defined(ALL_DB) if (mysql_query (_remote_SQL, show_query.c_str()) == 0) { if ((result = mysql_store_result (_remote_SQL)) == NULL) { _error_msg = "MirrorDatabase::LocalAddTable() - mysql_store_result: "; _error_msg = _error_msg + mysql_error (_remote_SQL) + "\n"; throw _error_msg.c_str(); } row = mysql_fetch_row (result); mysql_free_result(result); if(!row) throw "MirrorDatabase::LocalAddTable() - row is empty\n"; } else { _error_msg = "MirrorDatabase::LocalAddTable() - mysql_query: "; _error_msg = _error_msg + mysql_error (_remote_SQL) + "\n"; throw _error_msg.c_str(); } #ifdef ALL_DB if(dbIsMySQL()) { #endif // create it locally the MySQL way if(dbQueryExec(row[1]) != 0) { _error_msg = "MirrorDatabase::LocalAddTable() - dbQueryExec: "; _error_msg = _error_msg + dbErrorMsg() + "\n"; throw _error_msg.c_str(); } #ifdef ALL_DB } else { // create it locally the SQLite way std::string create_query = row[1]; std::string table_name, index_name, statement; std::string::size_type pos, pos2; int count = 1; //std::cerr << "BEFORE:\n" << create_query << std::endl; // remove all the ` characters while((pos = create_query.find("`")) != std::string::npos) { create_query.erase(pos,1); } // remove the type of database after the last ")" pos = create_query.rfind(")"); create_query.erase(pos+1, create_query.size()-pos); // get the name of the table, the third word pos = create_query.find(" "); pos = create_query.find(" ", pos+1); pos2 = create_query.find(" ", pos+1); create_query.replace(pos+1,(pos2-pos), _local_table); index_name = _local_table; index_name += "_index1"; //std::cerr << "The table name is " << _local_table << std::endl; // add a ); before the key pos = create_query.find("KEY"); if(pos != std::string::npos) { pos = create_query.rfind(",", pos); create_query.replace(pos, 1, ");"); } // change KEY to CREATE INDEX while((pos = create_query.find("KEY")) != std::string::npos) { create_query.replace(pos,3,"CREATE INDEX"); // get to the word after KEY pos2 = create_query.find(" ", pos+1); pos2 = create_query.find(" ", pos2+1); int pos3 = create_query.find(" ", pos2+1); int len = pos3-pos2; // add the number to the end of the index name char buf[64]; memset (buf, 0, sizeof (buf)); index_name.erase(index_name.size()-1,1); snprintf(buf, 64, "%s%d", index_name.c_str(), count); index_name = buf; // create the _index ON statement = index_name; statement += " ON "; statement += _local_table; create_query.replace(pos2+1, len, statement); create_query.replace(create_query.size()-2, 2, ";"); } //std::cerr << "AFTER:\n" << create_query << std::endl; // create it locally if(dbQueryExec((char*)create_query.c_str()) == 0) { _error_msg = "MirrorDatabase::LocalAddTable() - dbQueryExec: "; _error_msg = _error_msg + dbErrorMsg() + "\n"; throw _error_msg.c_str(); } } #endif #endif } } void MirrorDatabase::LocalForceAddTable(const char* table) { // just delete the table first if it exists if(LocalTableExists(table)) { std::string query = "DROP TABLE "; query += table; if(dbQueryExec((char*)query.c_str()) == 0) { _error_msg = "MirrorDatabase::LocalAddTable() - dbQueryExec: "; _error_msg = _error_msg + dbErrorMsg() + "\n"; throw _error_msg.c_str(); } } LocalAddTable(table); } /* Checks to see if all the fields are the same */ bool MirrorDatabase::SameSchemas(const char* table) { int remote_num = RemoteGetNumberFields(); int local_num; void* result; bool ret_val; std::string query = "SELECT * FROM "; query += table; if((result = dbQuery((char*)query.c_str())) == 0) { _error_msg = "MirrorDatabase::SameSchemas() - dbQuery: "; _error_msg = _error_msg + dbErrorMsg() + "\n"; throw _error_msg.c_str(); } local_num = dbNumberFields(result); if(remote_num == local_num) { // They maybe the same for(int i = 0; i < remote_num; i++) { if(strcmp(RemoteGetFieldName(i), dbFieldName(result, i)) != 0) ret_val = false; } ret_val = true; } else { ret_val = false; } dbFreeResult(result); return (ret_val); } void MirrorDatabase::LocalAddField (const char* fieldName) { _fields.push_back(fieldName); } void MirrorDatabase::LocalAddRow () throw(const char*) { if(_fields.size() != _values.size()) throw "MirrorDatabase::LocalAddRow() - There should be the same number of fields and values used.\n"; std::string field_str, value_str, query; char tmp[128]; memset (tmp, 0, sizeof (tmp)); for(unsigned int i = 0; i < _fields.size(); i++) { if(i == 0) { field_str = "("; value_str = "("; } field_str += _fields[i]; if(i != _fields.size()-1) { field_str += ", "; snprintf(tmp, 128, "'%s', ", _values[i].c_str()); } else { field_str += ")"; snprintf(tmp, 128, "'%s');", _values[i].c_str()); } value_str += tmp; } _fields.clear(); _values.clear(); query = "REPLACE INTO " + _local_table + field_str + " VALUES " + value_str; //std::cerr << "MirrorDatabase::LocalAddRow: query = " << query << std::endl; int rc = dbQueryExec ((char*) query.c_str ()); if (rc == 0 || dbBadQuery ()) { _error_msg = "MirrorDatabase::LocalAddRow() - "; _error_msg = _error_msg + dbErrorMsg () + "\n"; throw _error_msg.c_str(); } } bool MirrorDatabase::LocalTableExists(const char* table) { std::string query = "SELECT * FROM "; query += table; if(dbQueryExec((char*)query.c_str()) == 0) return false; else return true; } void MirrorDatabase::RemoteSetQuery (const char* query_str) { _remote_query = query_str; } void MirrorDatabase::RemoteExecute () throw(const char*) { if(_remote_SQL == NULL) { std::cerr << _error_msg << std::endl; _error_msg = "MirrorDatabase::RemoteExecute() - Database not connected\n"; throw _error_msg.c_str(); } if (mysql_query (_remote_SQL, _remote_query.c_str()) == 0) { if ((_remote_result = mysql_store_result (_remote_SQL)) == NULL) { _error_msg = "MirrorDatabase::RemoteExecute(): "; _error_msg = _error_msg + mysql_error (_remote_SQL) + "\n"; throw _error_msg.c_str(); } _remote_row = mysql_fetch_row (_remote_result); _remote_fields = mysql_fetch_fields(_remote_result); } else { _error_msg = "MirrorDatabase::RemoteExecute(): "; _error_msg = _error_msg + mysql_error (_remote_SQL) + "\n"; throw _error_msg.c_str(); } } unsigned int MirrorDatabase::RemoteGetNumberRows () { if(_remote_result != NULL) return mysql_num_rows (_remote_result); else return 0; } unsigned int MirrorDatabase::RemoteGetNumberFields () { if(_remote_result != NULL) return (mysql_num_fields (_remote_result)); else return 0; } const char* MirrorDatabase::RemoteGetFieldName (int column) { if(_remote_fields != NULL) return (_remote_fields[column].name); else return ""; } bool MirrorDatabase::RemoteIsValueNum(int column) { if (IS_NUM(_remote_fields[column].type)) return true; else return false; } const char* MirrorDatabase::RemoteGetFieldValue (int column) { if(_remote_row != NULL) { return _remote_row[column]; } else return ""; } void MirrorDatabase::RemoteNextRow() throw(const char*) { if(_remote_result != 0) { _remote_row = mysql_fetch_row (_remote_result); _remote_fields = mysql_fetch_fields(_remote_result); } else { _error_msg = "MirrorDatabase::RemoteNextRow() = _remote_result = NULL\n"; throw _error_msg.c_str(); } }