platform/src/Database.cpp

00001 // ------------------------------------------------------------------------
00002 // Pion is a development platform for building Reactors that process Events
00003 // ------------------------------------------------------------------------
00004 // Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.com)
00005 //
00006 // Pion is free software: you can redistribute it and/or modify it under the
00007 // terms of the GNU Affero General Public License as published by the Free
00008 // Software Foundation, either version 3 of the License, or (at your option)
00009 // any later version.
00010 //
00011 // Pion is distributed in the hope that it will be useful, but WITHOUT ANY
00012 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00013 // FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for
00014 // more details.
00015 //
00016 // You should have received a copy of the GNU Affero General Public License
00017 // along with Pion.  If not, see <http://www.gnu.org/licenses/>.
00018 //
00019 
00020 #include <pion/platform/Database.hpp>
00021 #include <pion/platform/DatabaseManager.hpp>
00022 #include <pion/platform/ConfigManager.hpp>
00023 #include <boost/filesystem/operations.hpp>
00024 
00025 namespace pion {        // begin namespace pion
00026 namespace platform {    // begin namespace platform (Pion Platform Library)
00027 
00028 
00029 // static members of Database
00030 
00031 const std::string           Database::INSERT_QUERY_ID = "urn:sql:insert-event";
00032 const std::string           Database::INSERT_IGNORE_QUERY_ID = "urn:sql:insert-ignore-event";
00033 const std::string           Database::BEGIN_QUERY_ID = "urn:sql:begin-transaction";
00034 const std::string           Database::COMMIT_QUERY_ID = "urn:sql:commit-transaction";
00035 
00036 const std::string           Database::MAP_ELEMENT_NAME = "TypeMap";
00037 const std::string           Database::PAIR_ELEMENT_NAME = "Pair";
00038 
00039 const std::string           Database::CLIENT_ELEMENT_NAME = "Client";
00040 const std::string           Database::BEGIN_ELEMENT_NAME = "BeginInsert";
00041 const std::string           Database::COMMIT_ELEMENT_NAME = "CommitInsert";
00042 const std::string           Database::CREATE_LOG_ELEMENT_NAME = "CreateLog";
00043 const std::string           Database::INSERT_LOG_ELEMENT_NAME = "InsertLog";
00044 const std::string           Database::ISOLATION_ELEMENT_NAME = "IsolationLevel";
00045 const std::string           Database::PRESQL_ELEMENT_NAME = "PreSQL";
00046 const std::string           Database::OPTION_ELEMENT_NAME = "Option";
00047 
00048 const std::string           Database::CREATE_STAT_ELEMENT_NAME = "CreateStat";
00049 const std::string           Database::UPDATE_STAT_ELEMENT_NAME = "UpdateStat";
00050 const std::string           Database::SELECT_STAT_ELEMENT_NAME = "SelectStat";
00051 
00052 const std::string           Database::DROP_INDEX_ELEMENT_NAME = "DropIndex";
00053 const std::string           Database::CREATE_INDEX_NORMAL_ELEMENT_NAME = "CreateIndexNormal";
00054 const std::string           Database::CREATE_INDEX_UNIQUE_ELEMENT_NAME = "CreateIndexUnique";
00055 const std::string           Database::CREATE_INDEX_CUSTOM_ELEMENT_NAME = "CreateIndexCustom";
00056 
00057 const std::string           Database::IGNORE_ATTRIBUTE_NAME = "ignore";
00058 const std::string           Database::OPTION_ATTRIBUTE_NAME = "option";
00059 
00060 const std::string           Database::INSERT_IGNORE_ELEMENT_NAME = "InsertIgnore";
00061 const std::string           Database::DROP_TABLE_ELEMENT_NAME = "DropTable";
00062 
00063 // Database member functions
00064 
00065 void Database::setConfig(const Vocabulary& v, const xmlNodePtr config_ptr)
00066 {
00067     PlatformPlugin::setConfig(v, config_ptr);
00068 }
00069 
00071 const boost::regex inline getRegex(const std::string& str)
00072 {
00073     return (str.empty() ? boost::regex() : boost::regex(str));
00074 }
00075 
00076 void Database::readConfigDetails(const xmlNodePtr config_ptr)
00077 {
00078     if (! ConfigManager::getConfigOption(CLIENT_ELEMENT_NAME, m_database_client, config_ptr))
00079         throw DatabaseClientException(getId());
00080 
00081     m_begin_insert.clear();
00082     m_commit_insert.clear();
00083     ConfigManager::getConfigOption(BEGIN_ELEMENT_NAME, m_begin_insert, config_ptr);
00084 
00085     ConfigManager::getConfigOption(COMMIT_ELEMENT_NAME, m_commit_insert, config_ptr);
00086 
00087     if (! ConfigManager::getConfigOption(CREATE_LOG_ELEMENT_NAME, m_create_log, config_ptr))
00088         throw DatabaseConfigMissing(CREATE_LOG_ELEMENT_NAME);
00089 
00090     // CREATE_LOG_ELEMENT_NAME was already found, check if "ignore" attribute exists...
00091     m_create_log_attr = getRegex(ConfigManager::getAttribute(IGNORE_ATTRIBUTE_NAME, ConfigManager::findConfigNodeByName(CREATE_LOG_ELEMENT_NAME, config_ptr)));
00092 
00093     if (! ConfigManager::getConfigOption(INSERT_LOG_ELEMENT_NAME, m_insert_log, config_ptr))
00094         throw DatabaseConfigMissing(INSERT_LOG_ELEMENT_NAME);
00095 
00096     m_insert_log_attr = getRegex(ConfigManager::getAttribute(IGNORE_ATTRIBUTE_NAME, ConfigManager::findConfigNodeByName(INSERT_LOG_ELEMENT_NAME, config_ptr)));
00097 
00098     if (! ConfigManager::getConfigOption(CREATE_STAT_ELEMENT_NAME, m_create_stat, config_ptr))
00099         throw DatabaseConfigMissing(CREATE_STAT_ELEMENT_NAME);
00100 
00101     m_create_stat_attr = getRegex(ConfigManager::getAttribute(IGNORE_ATTRIBUTE_NAME, ConfigManager::findConfigNodeByName(CREATE_STAT_ELEMENT_NAME, config_ptr)));
00102 
00103     if (! ConfigManager::getConfigOption(UPDATE_STAT_ELEMENT_NAME, m_update_stat, config_ptr))
00104         throw DatabaseConfigMissing(UPDATE_STAT_ELEMENT_NAME);
00105 
00106     m_update_stat_attr = getRegex(ConfigManager::getAttribute(IGNORE_ATTRIBUTE_NAME, ConfigManager::findConfigNodeByName(UPDATE_STAT_ELEMENT_NAME, config_ptr)));
00107 
00108     if (! ConfigManager::getConfigOption(SELECT_STAT_ELEMENT_NAME, m_select_stat, config_ptr))
00109         throw DatabaseConfigMissing(SELECT_STAT_ELEMENT_NAME);
00110 
00111     m_select_stat_attr = getRegex(ConfigManager::getAttribute(IGNORE_ATTRIBUTE_NAME, ConfigManager::findConfigNodeByName(SELECT_STAT_ELEMENT_NAME, config_ptr)));
00112 
00113     if (! ConfigManager::getConfigOption(DROP_INDEX_ELEMENT_NAME, m_drop_index, config_ptr))
00114         throw DatabaseConfigMissing(DROP_INDEX_ELEMENT_NAME);
00115 
00116     m_drop_index_attr = getRegex(ConfigManager::getAttribute(IGNORE_ATTRIBUTE_NAME, ConfigManager::findConfigNodeByName(DROP_INDEX_ELEMENT_NAME, config_ptr)));
00117 
00118     if (! ConfigManager::getConfigOption(CREATE_INDEX_NORMAL_ELEMENT_NAME, m_create_index_normal, config_ptr))
00119         throw DatabaseConfigMissing(CREATE_INDEX_NORMAL_ELEMENT_NAME);
00120 
00121     m_create_index_normal_attr = getRegex(ConfigManager::getAttribute(IGNORE_ATTRIBUTE_NAME, ConfigManager::findConfigNodeByName(CREATE_INDEX_NORMAL_ELEMENT_NAME, config_ptr)));
00122 
00123     if (! ConfigManager::getConfigOption(CREATE_INDEX_UNIQUE_ELEMENT_NAME, m_create_index_unique, config_ptr))
00124         throw DatabaseConfigMissing(CREATE_INDEX_UNIQUE_ELEMENT_NAME);
00125 
00126     m_create_index_unique_attr = getRegex(ConfigManager::getAttribute(IGNORE_ATTRIBUTE_NAME, ConfigManager::findConfigNodeByName(CREATE_INDEX_UNIQUE_ELEMENT_NAME, config_ptr)));
00127 
00128     if (! ConfigManager::getConfigOption(CREATE_INDEX_CUSTOM_ELEMENT_NAME, m_create_index_custom, config_ptr))
00129         throw DatabaseConfigMissing(CREATE_INDEX_CUSTOM_ELEMENT_NAME);
00130 
00131     m_create_index_custom_attr = getRegex(ConfigManager::getAttribute(IGNORE_ATTRIBUTE_NAME, ConfigManager::findConfigNodeByName(CREATE_INDEX_CUSTOM_ELEMENT_NAME, config_ptr)));
00132 
00133     if (! ConfigManager::getConfigOption(INSERT_IGNORE_ELEMENT_NAME, m_insert_ignore, config_ptr))
00134         throw DatabaseConfigMissing(INSERT_IGNORE_ELEMENT_NAME);
00135 
00136     m_insert_ignore_attr = getRegex(ConfigManager::getAttribute(IGNORE_ATTRIBUTE_NAME, ConfigManager::findConfigNodeByName(INSERT_IGNORE_ELEMENT_NAME, config_ptr)));
00137 
00138     if (! ConfigManager::getConfigOption(DROP_TABLE_ELEMENT_NAME, m_drop_table, config_ptr))
00139         throw DatabaseConfigMissing(DROP_TABLE_ELEMENT_NAME);
00140 
00141     m_drop_table_attr = getRegex(ConfigManager::getAttribute(IGNORE_ATTRIBUTE_NAME, ConfigManager::findConfigNodeByName(DROP_TABLE_ELEMENT_NAME, config_ptr)));
00142 
00143     // If IsolationLevel is not defined, assume ReadUncommitted
00144     // If IsolationLevel is defined, make sure it matches, otherwise throw exception
00145     std::string isolation_level_str;
00146     if (ConfigManager::getConfigOption(ISOLATION_ELEMENT_NAME, isolation_level_str, config_ptr)) {
00147         if (isolation_level_str == "ReadUncommitted")
00148             m_isolation_level = IL_ReadUncommitted;
00149         else if (isolation_level_str == "ReadCommitted")
00150             m_isolation_level = IL_ReadCommitted;
00151         else if (isolation_level_str == "RepeatableRead")
00152             m_isolation_level = IL_RepeatableRead;
00153         else if (isolation_level_str == "Serializable")
00154             m_isolation_level = IL_Serializable;
00155         else
00156             throw InvalidIsolationLevel(isolation_level_str);
00157     } else
00158         m_isolation_level = IL_ReadUncommitted;
00159 //  Setting the undefined default to Read Uncommitted for performance
00160 //      m_isolation_level = IL_LevelUnknown;
00161 
00162     m_sql_affinity.clear();
00163     m_sql_affinity.resize(Vocabulary::TYPE_OBJECT + 1);  // TODO: this depends on TYPE_OBJECT being last; should do something better here.
00164     xmlNodePtr mapping_node;
00165     if ((mapping_node = ConfigManager::findConfigNodeByName(MAP_ELEMENT_NAME, config_ptr)) != NULL) {
00166         xmlNodePtr map_pair_node = mapping_node->children;
00167         std::string map_pair_str;
00168         while (ConfigManager::getConfigOption(PAIR_ELEMENT_NAME, map_pair_str, map_pair_node)) {
00169             // Pairs are: VocabTerm,SQLtype
00170             std::string::size_type loc = map_pair_str.find(',');
00171             if (loc != std::string::npos) {
00172                 pion::platform::Vocabulary::DataType i = Vocabulary::parseDataType(map_pair_str.substr(0, loc));
00173                 m_sql_affinity[i] = map_pair_str.substr(loc + 1);
00174             } else
00175                 throw BadTypePair(map_pair_str);
00176             map_pair_node = map_pair_node->next;
00177         }
00178     } else
00179         throw MissingTypeMap(getId());
00180 
00181     // Optional PreSQL section -- just fill in the m_pre_sql array
00182     m_pre_sql.clear();
00183     m_pre_sql_attr.clear();
00184     xmlNodePtr presql_node;
00185     if ((presql_node = ConfigManager::findConfigNodeByName(PRESQL_ELEMENT_NAME, config_ptr)) != NULL) {
00186         std::string presql_str;
00187         while (ConfigManager::getConfigOption(PRESQL_ELEMENT_NAME, presql_str, presql_node)) {
00188             m_pre_sql.push_back(presql_str);
00189             m_pre_sql_attr.push_back(getRegex(ConfigManager::getAttribute(IGNORE_ATTRIBUTE_NAME, presql_node)));
00190             presql_node = presql_node->next;
00191         }
00192     }
00193 
00194     // Optional Option section -- fill in the m_options array
00195     m_options.clear();
00196     m_options_values.clear();
00197     xmlNodePtr option_node;
00198     if ((option_node = ConfigManager::findConfigNodeByName(OPTION_ELEMENT_NAME, config_ptr)) != NULL) {
00199         std::string option_value_str;
00200         while (ConfigManager::getConfigOption(OPTION_ELEMENT_NAME, option_value_str, option_node)) {
00201             m_options_values.push_back(option_value_str);
00202             m_options.push_back(ConfigManager::getAttribute(OPTION_ATTRIBUTE_NAME, option_node));
00203             option_node = option_node->next;
00204         }
00205     }
00206 }
00207 
00209 void Database::readConfig(const xmlNodePtr config_ptr, std::string engine_str)
00210 {
00211     // Assume embedded configuration...
00212     xmlNodePtr config_detail_ptr = config_ptr;
00213 
00214     // If client name is not supplied, use engine name as client name
00215     // compatibility for SQLite == SQLite
00216     if (m_database_engine.empty())
00217         m_database_engine = engine_str;
00218     if (m_database_client.empty())
00219         m_database_client = engine_str;
00220 
00221     xmlDocPtr template_doc_ptr = NULL;
00222     if (! ConfigManager::getConfigOption(CLIENT_ELEMENT_NAME, m_database_client, config_ptr)) {
00223         if ((template_doc_ptr = getDatabaseManager().getDatabaseEngineConfig(m_database_engine, config_detail_ptr)) == NULL)
00224             throw ReadConfigException(getId());
00225     }
00226     readConfigDetails(config_detail_ptr);
00227 
00228     if (template_doc_ptr != NULL)
00229         xmlFreeDoc(template_doc_ptr);
00230 }
00231 
00233 void Database::stringReplace(std::string& src, const char* search, const std::string& substitute)
00234 {
00235     std::string::size_type i = 0;
00236     while ((i = src.find(search, i)) != std::string::npos)
00237         src.replace(i, strlen(search), substitute);
00238 }
00239 
00241 std::string& Database::stringSubstitutes(std::string& query, const pion::platform::Query::FieldMap& field_map, const std::string& table_name, const std::string& columns_override)
00242 {
00243     // Substitute any table name instances in query
00244     stringReplace(query, ":TABLE", table_name);
00245 
00246     std::string col, fields, columns, questions, params;
00247     for (unsigned int p = 0; p < field_map.size(); p++) {
00248         fields += field_map[p].first + ' ' +
00249                     m_sql_affinity[field_map[p].second.term_type];
00250 // Now using m_sql_affinity[] table instead of a lookup function
00251 //                  getSQLAPIAffinity(field_it->second.second.term_type);
00252         columns += field_map[p].first;
00253         if (p == 0)
00254             col = field_map[p].first;
00255         questions += '?';
00256         params += ':' + boost::lexical_cast<std::string>(p+1);  // Params are 1-based
00257         if (p+1 < field_map.size()) {           // Add commas, but not after last
00258             fields += ',';
00259             columns += ',';
00260             questions += ',';
00261             params += ',';
00262         }
00263     }
00264     stringReplace(query, ":FIELDS", fields);        // Sub any field name sequences
00265     stringReplace(query, ":COLUMNS", columns_override.empty() ? columns : columns_override);    // Sub column instances
00266     stringReplace(query, ":COLUMN", col);           // Sub single col instances
00267     stringReplace(query, ":QUESTIONS", questions);  // Sub question mark sequences
00268     stringReplace(query, ":PARAMS", params);        // :1,:2,:3, etc...
00269     return query;
00270 }
00271 
00272 }   // end namespace platform
00273 }   // end namespace pion

Generated on Wed Apr 13 16:38:34 2011 for pion-platform by  doxygen 1.4.7