// SqlGenericRepository.cxx #include "dataset_catalog/SqlGenericRepository.h" #include #include #include #include "dataset_sql/SqlResultTable.h" #include "dataset_catalog/ConnectionResolver.h" #include "dataset_catalog/CacheGenericRepository.h" using std::string; using std::cerr; using std::endl; using std::istringstream; using std::ostringstream; using dset::SqlTable; using dset::SqlResult; using dset::SqlResultTable; using dset::ConnectionResolver; using dset::GenericRepository; using dset::SqlGenericRepository; using dset::CacheGenericRepository; typedef SqlResult::Row Row; typedef SqlResult::NameList NameList; typedef SqlGenericRepository::size_type size_type; typedef std::map EleMap; //********************************************************************** // Local Definitions. //********************************************************************** namespace { //********************************************************************** // Creator. GenericRepository* create(string type, string conn) { string prefix = "SqlGenericRepository::create: "; // Use the catalog resolver to resolve the name and password. ConnectionResolver rs; string resconn = rs.resolve(type, conn, true); if ( resconn.empty() ) { cerr << prefix << "Unable to resolve connection " << conn << " for type " << type << endl; return 0; } string::size_type ipos = resconn.find(":"); string name = resconn.substr(0, ipos); string params = resconn.substr(ipos+1); SqlTable* ptab = SqlTable::create(name, params); if ( ptab == 0 ) { cerr << prefix << "Unable to find table" << endl; cerr << " Name: " << name << endl; cerr << " Parameters: " << params << endl; return 0; } if ( ! ptab->is_valid() ) { cerr << prefix << "Invalid table" << endl; cerr << " Name: " << name << endl; cerr << " Parameters: " << params << endl; delete ptab; return 0; } GenericRepository* prep = new SqlGenericRepository(ptab); if ( ! prep->is_valid() ) { cerr << prefix << "Invalid repository" << endl; delete prep; return 0; } if ( prep->repository_type() != type ) { cerr << prefix << "Repository has the wrong type" << endl; cerr << " Expected: " << type << endl; cerr << " Found: " << prep->repository_type() << endl; delete prep; return 0; } // If this is a cache, then create the primary. if ( prep->cache_connection().size() ) { prep = new CacheGenericRepository(prep); if ( ! prep->is_valid() ) { cerr << prefix << "Invalid cache repository" << endl; delete prep; return 0; } } return prep; } //********************************************************************** // SqlTable creator callback. // When a new SqlTable name is registered, we register the above // generic repository creator under the same name. void sql_table_register_callback(string name) { GenericRepository::register_creator(name, create); } //********************************************************************** // Function to register all existing SqlTable names and to register // the callback funtion to catch any future names. int register_all() { for ( SqlTable::CreatorList::const_iterator inam = SqlTable::creators().begin(); inam != SqlTable::creators().end(); ++inam ) { GenericRepository::register_creator(*inam, create); } SqlTable::register_creator_callback(sql_table_register_callback); return 0; } // Make registrations. int regstat = register_all(); //********************************************************************** // Return the corresponding id field present in the table void get_idfields(const NameList& cols, string& sidh, string& sidl) { for ( NameList::const_iterator icol = cols.begin(); icol!=cols.end(); ++icol ) { string name = *icol; if ( name == "uid" ) { sidh = "uid"; sidl = ""; return; } if ( name == "idhi" ) { sidh = "idhi"; sidl = "idlo"; return; } if ( name == "vidh" ) { sidh = "vidh"; sidl = "vidl"; return; } } sidh = ""; sidl = ""; } //********************************************************************** // Split the id field into its respective components void decompose_id(string id, string &idh, string& idl) { string::size_type ipos = id.find("-"); idh = id.substr(0, ipos); idl = id.substr(ipos+1); } //********************************************************************** // Return constraint to select an ID. string idconstraint(const SqlTable* ptab, string id) { assert( ptab != 0 ); NameList cols = ptab->get_schema().get_col_names(); string sidh, sidl; get_idfields(cols, sidh, sidl); if ( sidh == "" ) return ""; if ( sidl == "" ) return sidh + " = '" + id + "'"; string vidh, vidl; decompose_id(id, vidh, vidl); return sidh + " = '" + vidh + "' AND " + sidl + " = '" + vidl + "'"; } //********************************************************************** // Return constraint to select any ID. string anyidconstraint(const SqlTable* ptab) { assert( ptab != 0 ); NameList cols = ptab->get_schema().get_col_names(); string sidh, sidl; get_idfields(cols, sidh, sidl); if ( sidh == "" ) return ""; if ( sidl == "" ) return sidh + " != '0-0'"; return sidh + " != '0'"; } //********************************************************************** // Extract an ID from a row. string rowid(string sidh, string sidl, const Row& row) { string sid; assert( sidh.size() ); Row::const_iterator ival = row.find(sidh); assert( ival != row.end() ); sid = ival->second; assert( sid.size() ); if ( sidl.size() ) { ival = row.find(sidl); assert( ival != row.end() ); sid += "-"; sid += ival->second; } return sid; } //********************************************************************** } // end unnamed namespace //********************************************************************** // Member functions. //********************************************************************** // Managing constructor. SqlGenericRepository::SqlGenericRepository(SqlTable* ptable) : m_ptable(ptable), m_manage(true) { m_valid = m_ptable!=0 && m_ptable->is_valid() && repository_type().size(); } //********************************************************************** // Constructor. SqlGenericRepository::SqlGenericRepository(SqlTable& table) : m_ptable(&table), m_manage(false) { m_valid = m_ptable!=0 && m_ptable->is_valid() && repository_type().size(); } //********************************************************************** // Destructor. SqlGenericRepository::~SqlGenericRepository() { if ( m_manage ) { delete m_ptable; } } //********************************************************************** // Const members. //********************************************************************** // Validity. bool SqlGenericRepository::is_valid() const { return m_valid; } //********************************************************************** // Return the type of repository. string SqlGenericRepository::repository_type() const { string desc = get("0-0"); Text::WordList words = Text::split(desc); if ( words.size() == 0 ) { return ""; } return words[0]; } //********************************************************************** // Return the cache connection string. string SqlGenericRepository::cache_connection() const { string desc = get("0-0"); Text::WordList words = Text::split(desc); if ( words.size() < 2 ) { return ""; } return words[1]; } //********************************************************************** // Error. int SqlGenericRepository::error() const { return m_ptable->error(); } //********************************************************************** // Error string. string SqlGenericRepository::error_string(int ecode) const { return m_ptable->error_string(ecode); } //********************************************************************** // Is an element in the repository? // Returns true if element is present. bool SqlGenericRepository::has(string id) const { // Check if id is empty if ( ! id.size() ) return false; string constraint = idconstraint(m_ptable, id); if ( ! constraint.size() ) return false; SelectQuery sql("", constraint); SqlResult res = m_ptable->execute_select_query(sql); if ( res.num_rows() == 0 ) return false; return true; } //********************************************************************** // Retrieve an element from repository. string SqlGenericRepository::get(string id) const { // Check if id is empty if ( id.empty()) return ""; string constraint = idconstraint(m_ptable, id); if ( ! constraint.size() ) return ""; SelectQuery sql("", constraint); SqlResult res = m_ptable->execute_select_query(sql); if ( res.num_rows() == 0 ) return ""; // Since query is based on id, there is only 1 row in result Row row = res.fetch_row(0); assert(row.size() != 0); for ( Row::const_iterator it = row.begin(); it != row.end(); ++it ) { if ( it->first == "sxml" ) return it->second; } return ""; } //********************************************************************** // Retrieve a list of elements. int SqlGenericRepository:: mget(const IdList& ids, ElementList& eles) const { // For SqlResult table, use the base method. // SqlResultTable cannot handle complex queries. if ( dynamic_cast(m_ptable) ) { return GenericRepository::mget(ids, eles); } int nbad = 0; // Build query to fetch all elements. string qstr; for ( IdList::const_iterator iid=ids.begin(); iid!=ids.end(); ++iid ) { string id = *iid; // Check if id is empty if ( id.empty()) { cerr << "SqlGenericRepository::mget: " << "Ignoring empty ID" << endl; continue; } string con = idconstraint(m_ptable, id); string wcon = "(" + con + ")"; if ( qstr.size() ) { qstr += " OR " + wcon; } else { qstr = wcon; } } // Execute query. SelectQuery sql("", qstr); SqlResult res = m_ptable->execute_select_query(sql); // Build map of elements from result. EleMap elemap; string sidh; string sidl; NameList cols = m_ptable->get_schema().get_col_names(); get_idfields(cols, sidh, sidl); for ( int irow=0; irow!=res.num_rows(); ++irow ) { Row row = res.fetch_row(irow); assert(row.size() != 0); string sid = rowid(sidh, sidl, row); Row::const_iterator ival = row.find("sxml"); assert( ival != row.end() ); string sxml = ival->second; elemap[sid] = sxml; } // Loop over map and fill output list. for ( IdList::const_iterator iid=ids.begin(); iid!=ids.end(); ++iid ) { string sid = *iid; string sele; EleMap::const_iterator iele = elemap.find(sid); if ( iele != elemap.end() ) { sele = iele->second; eles.push_back(sele); } else { ++nbad; } } return nbad; } //********************************************************************** // Retrieve modification time. time_t SqlGenericRepository::time(string id) const { // Check if id is empty if ( id.empty()) return 0; string constraint = idconstraint(m_ptable, id); if ( ! constraint.size() ) return 0; // Fetch the unix modtime column description. string cmodtime = m_ptable->modify_time_column(); SelectQuery sql(cmodtime, constraint); SqlResult res = m_ptable->execute_select_query(sql); if ( res.num_rows() == 0 ) return 0; // Since query is based on id, there is only 1 row in result Row row = res.fetch_row(0); assert(row.size() < 2); time_t time = 0; if ( row.size() == 1 ) { string stime = row.begin()->second; istringstream sstime(stime); sstime >> time; } return time; } //********************************************************************** // Return the size of the repository. size_type SqlGenericRepository::size() const { return m_ptable->get_row_count() - 1; } //********************************************************************** // Return the size of the repository since. size_type SqlGenericRepository::size_since(time_t time) const { ostringstream sscon; sscon << anyidconstraint(m_ptable); string cmodtime = m_ptable->modify_time_column(); if ( ! cmodtime.size() ) { cerr << "SqlGenericRepository::size_since: " << "Table does not hold modification time" << endl; return 0; } sscon << " AND " << cmodtime << " >= " << time; SelectQuery sel("", sscon.str()); return m_ptable->get_row_count(sel); } //********************************************************************** // Get all the IDs in the repository. GenericRepository::IdList SqlGenericRepository::get_ids(size_type maxent) const { return get_ids_since(0, maxent); } //********************************************************************** // Get all the IDs in the repository since. GenericRepository::IdList SqlGenericRepository::get_ids_since( time_t time, size_type maxent) const { GenericRepository::IdList ids; // Extract the appropriate idfields string sidh, sidl; NameList cols = m_ptable->get_schema().get_col_names(); get_idfields(cols, sidh, sidl); // Construct the query // ...column names string columns = sidh; if ( sidl.size() ) { columns += "," + sidl; } // ...constraint ostringstream sscon; sscon << anyidconstraint(m_ptable); if ( time > 0 ) { string cmodtime = m_ptable->modify_time_column(); if ( ! cmodtime.size() ) { cerr << "SqlGenericRepository::get_ids_since: " << "Table does not hold modification time" << endl; return ids; } sscon << " AND " << cmodtime << " >= " << time; } sscon << " limit " << maxent+1; SelectQuery sql(columns, sscon.str()); SqlResult res = m_ptable->execute_select_query(sql); if ( res.num_rows() == 0 ) return ids; for ( size_type index = 0; index < res.num_rows(); ++index ) { Row row = res.fetch_row(index); string id = row[sidh]; if ( sidl.size() ) { id += "-" + row[sidl]; } assert( id != "0-0" ); if ( ids.size() >= maxent ) break; ids.push_back(id); } return ids; } //********************************************************************** // Const memebers. //********************************************************************** // Insert a new element into repository. // Returns 0 on success. int SqlGenericRepository::insert(string id, string ele) const { // Check if id is empty if ( id.empty() ) return 1; if ( ele.empty() ) return 2; // Check authorization. if ( ! authorized(ele) ) return 9; // Make sure ID is not already present. if ( has(id) ) { return 3; } // Extract the appropriate field names. NameList cols = m_ptable->get_schema().get_col_names(); string sidh, sidl; get_idfields(cols, sidh, sidl); // Construct the insert query Row row; if ( sidl.size() == 0 ) { row[sidh] = id; } else { string vidh, vidl; decompose_id(id, vidh, vidl); row[sidh] = vidh; row[sidl] = vidl; } row["sxml"] = ele; InsertQuery sql(row); if ( m_ptable->execute_insert_query(sql) != 0 ) return 3; return 0; } //********************************************************************** // Delete an element from repository. // Returns 0 on success. int SqlGenericRepository::remove(string id) const { // Check if id is empty if ( id.empty() ) return 1; // Make sure ID is already present. if ( ! has(id) ) { return 2; } // Check ownership authorization. if ( ! authorized(get(id)) ) return 9; string constraint = idconstraint(m_ptable, id); DeleteQuery sql; sql.set(constraint); if ( m_ptable->execute_delete_query(sql) != 0 ) return 3; return 0; } //**********************************************************************