#ifndef __SERVICE_SPACE_DB2O_DB_H__ #define __SERVICE_SPACE_DB2O_DB_H__ #include "../ServiceSpaceLib/StdHdr.h" #include "../ServiceSpaceLib/utilib/int2type.h" #include "../ServiceSpaceLib/utilib/to_value.h" #include "../ServiceSpaceLib/utilib/noncopyable.h" #include "../ServiceSpaceLib/utilib/split.h" #include "../ServiceSpaceLib/utilib/stringN.h" #include "../ServiceSpaceLib/utilib/vectorN.h" #include "field.h" #include "object.h" #include "type2sqlstring.h" #include #include #include #include #include #include #include __SERVICE_SPACE_BEGIN_NS__ using namespace utilib; enum { DB_MYSQL = 0 }; template class dbdesc; template<> struct dbdesc { public: char dbname[64]; char charset[32]; char host[32]; char user[32]; char passwrd[32]; int port; int flags; }; template struct table_info { std::string name; std::vector fields; bool checked; }; template<> class database : public noncopyable { public: enum { MAX_SQL_LEN = 64 * 1024}; class except : public std::exception { public: except(MYSQL * mysql, const std::string & desc)throw() : errcode_((unsigned)-1) { desc_ = "[MYSQL]"; desc_ += desc; if (mysql) { desc_ += " | ("; errcode_ = mysql_errno(mysql); char buf[32]; sprintf(buf, "%u", errcode_); desc_ += buf; desc_ += ") "; desc_ += mysql_error(mysql); } } ~except()throw() { } const unsigned errcode()const { return errcode_; } const char * what()const throw() { return desc_.c_str(); } private: unsigned errcode_; std::string desc_; }; template struct set_field_value { set_field_value(const char * _name, const char * _value) : name(_name) , value(_value) { } template void operator()(Field & fl) { if (value && (0 == ::strcmp(name, Field::name()))) { to_value(value, fl.value); fl.inited(true); } } const char * name; const char * value; }; public: database(const dbdesc & desc) :desc_(desc) { try { connect(desc); open(desc); }catch(const std::exception &) { disconnect(); throw; }catch(...) { throw; } } ~database() { disconnect(); } void ping() { if (0 != mysql_ping(&mysql_)) { disconnect(); try { connect(desc_); open(desc_); }catch(const std::exception &) { disconnect(); throw; }catch(...) { throw; } } } template void insert(const Object & obj, unsigned & err) { try { insert(obj); }catch(const except & e) { err = e.errcode(); }catch(...) { err = -1; } } template void insert(const Object & obj) { ping(); create(obj); char sql[MAX_SQL_LEN]; build_insert_sql(obj, sql); if (mysql_query(&mysql_, sql)) { std::string desc = "failed to insert obj["; desc += Object::name(); desc += "]"; throw except(&mysql_, desc); } my_ulonglong num_rows = mysql_affected_rows(&mysql_); if (num_rows != 1) { std::string desc = "failed to insert obj["; desc += Object::name(); desc += "]"; throw except(&mysql_, desc); } } template void replace(const Object & obj, unsigned & err) { try { replace(obj); }catch(const except & e) { err = e.errcode(); }catch(...) { err = -1; } } template void replace(const Object & obj) { ping(); create(obj); char sql[MAX_SQL_LEN]; build_replace_sql(obj, sql); if (mysql_query(&mysql_, sql)) { std::string desc = "failed to replace obj["; desc += Object::name(); desc += "]"; throw except(&mysql_, desc); } my_ulonglong num_rows = mysql_affected_rows(&mysql_); if (num_rows == 0) { std::string desc = "failed to replace obj["; desc += Object::name(); desc += "]"; throw except(&mysql_, desc); } } template int update(Object & obj, unsigned & err) { try { return update(obj); }catch(const except & e) { err = e.errcode(); }catch(...) { err = -1; } } template my_ulonglong update(Object & obj) { ping(); create(obj); char sql[MAX_SQL_LEN]; build_update_sql(obj, sql); SS_DEBUG(ACE_TEXT("UPDATA SQL == %s \n"),sql) ; if (mysql_query(&mysql_, sql)) { std::string desc = "failed to update obj["; desc += Object::name(); desc += "]"; throw except(&mysql_, desc); } return mysql_affected_rows(&mysql_); } template int query(Object & obj, unsigned & err, int offset = 0, int rows = 0) { try { return query(obj, offset, rows); }catch(const except & e) { err = e.errcode(); }catch(...) { err = -1; } } template int query(Object & obj, int offset = 0, int rows = 0, const char * append = 0) { ping(); create(obj); char sql[MAX_SQL_LEN]; build_query_sql(obj, sql); if (append) { sprintf(sql + strlen(sql), " %s", append); } if (rows) { sprintf(sql + strlen(sql), " limit %d, %d", offset, rows); } if (mysql_query(&mysql_, sql)) { std::string desc = "failed to query obj["; desc += Object::name(); desc += "]"; throw except(&mysql_, desc); } MYSQL_RES *result = 0; unsigned int num_fields = 0; result = mysql_store_result(&mysql_); if (result) num_fields = mysql_num_fields(result); else { std::string desc = "failed to query obj["; desc += Object::name(); desc += "]"; throw except(&mysql_, desc); } MYSQL_FIELD *field; std::vector fields; while((field = mysql_fetch_field(result))) { fields.push_back(field->name); } MYSQL_ROW row = 0; Object * cur_obj = &obj; int count = 0; while((row = mysql_fetch_row(result))) { if (count != 0) { Object * tmp = 0; try{ tmp = new Object(); if (!tmp) throw std::bad_alloc(); }catch(...) { break; } cur_obj->next(tmp); cur_obj = tmp; } for(unsigned int i = 0; i < num_fields; ++i) { set_field_value fv(fields[i].c_str(), row[i]); foreach((typename Object::tuple_type &)*cur_obj, fv); } ++count; } mysql_free_result(result); return count; } template int remove(const Object & obj, unsigned & err) { try{ return remove(obj); }catch(const except & e) { err = e.errcode(); }catch(...) { err = -1; } } template int remove(const Object & obj) { ping(); create(obj); char sql[MAX_SQL_LEN]; build_remove_sql(obj, sql); if (mysql_query(&mysql_, sql)) { std::string desc = "failed to delete obj mysql["; desc += Object::name(); desc += "]"; throw except(&mysql_, desc); } return mysql_affected_rows(&mysql_); } template void create(const Object & obj) { std::map >::iterator it = tables_.find(Object::name()); /*std::vector::iterator it = std::find_if( tables_.begin(), tables_.end(), std::bind1st, std::string>( std::equal_to(), obj.name()));*/ if (it != tables_.end()) { if (it->second.checked) return; _check_table_field check_table(it->second); foreach((typename Object::tuple_type &)obj, check_table); it->second.checked = true; return ; } char sql[512]; build_create_table_sql(obj, sql); if (0 != mysql_query(&mysql_, sql)) { std::string desc = "failed to create table mysql[table:"; desc += Object::name(); desc += "]"; throw except(&mysql_, desc); } table_info ti; ti.name = Object::name(); ti.checked = true; tables_[ti.name] = ti; } template const unsigned get_count() { Object * obj = 0; ping(); create(*obj); char sql[MAX_SQL_LEN]; sprintf(sql, "select count(*) from %s", Object::name()); if (mysql_query(&mysql_, sql)) { std::string desc = "failed to delete obj mysql["; desc += Object::name(); desc += "]"; throw except(&mysql_, desc); } MYSQL_RES *result = 0; unsigned int num_fields = 0; result = mysql_store_result(&mysql_); if (result) num_fields = mysql_num_fields(result); else { std::string desc = "failed to query obj["; desc += Object::name(); desc += "]"; throw except(&mysql_, desc); } MYSQL_ROW row = 0; unsigned count = 0; while((row = mysql_fetch_row(result))) { for(unsigned int i = 0; i < num_fields; ++i) { to_value(row[i], count); break; } break; } mysql_free_result(result); return count; } template const typename Field::value_type get_max_value() { Object * obj = 0; ping(); create(*obj); char sql[MAX_SQL_LEN]; sprintf(sql, "select max(%s) from %s", Field::name(), Object::name()); if (mysql_query(&mysql_, sql)) { std::string desc = "failed to delete obj mysql["; desc += Object::name(); desc += "]"; throw except(&mysql_, desc); } MYSQL_RES *result = 0; unsigned int num_fields = 0; result = mysql_store_result(&mysql_); if (result) num_fields = mysql_num_fields(result); else { std::string desc = "failed to query obj["; desc += Object::name(); desc += "]"; throw except(&mysql_, desc); } MYSQL_ROW row = 0; typename Field::value_type value = typename Field::value_type(); while((row = mysql_fetch_row(result))) { for(unsigned int i = 0; i < num_fields; ++i) { if (row[i]) to_value(row[i], value); break; } break; } mysql_free_result(result); return value; } private: void connect(const dbdesc & desc) { if (!mysql_init(&mysql_)) throw except(0,"failed to init mysql"); if (!mysql_real_connect(&mysql_, desc.host, desc.user, desc.passwrd, 0, desc.port, NULL, desc.flags)) { std::string errdesc = "failed to connect mysql[host:"; errdesc += desc.host; errdesc += " db:"; errdesc += desc.dbname; errdesc += " user:"; errdesc += desc.user; errdesc += " passwrd:"; errdesc += " port:"; char buf[32]; sprintf(buf, "%d", desc.port); errdesc += buf; errdesc += "]"; throw except(&mysql_, errdesc); } } void disconnect() { tables_.clear(); mysql_close(&mysql_); } void open(const dbdesc & desc) { char sql[128]; sprintf(sql, "create database if not exists %s", desc.dbname); if (desc.charset[0]) sprintf(sql + ::strlen(sql), " character set %s", desc.charset); if (0 != mysql_query(&mysql_, sql)) { std::string errdesc = "failed to open mysql[dbname:"; errdesc += desc.dbname; errdesc += "]"; throw except(&mysql_, errdesc); } sprintf(sql, "use %s", desc.dbname); if (0 != mysql_query(&mysql_, sql)) { std::string errdesc = "failed to use mysql[dbname:"; errdesc += desc.dbname; errdesc += "]"; throw except(&mysql_, errdesc); } sprintf(sql, "show tables"); if (0 != mysql_query(&mysql_, sql)) { std::string errdesc = "failed to show tables mysql[dbname:"; errdesc += desc.dbname; errdesc += "]"; throw except(&mysql_, errdesc); } MYSQL_RES *result = 0; unsigned int num_fields = 0; result = mysql_store_result(&mysql_); if (result) // there are rows { num_fields = mysql_num_fields(result); while(true) { MYSQL_ROW row = mysql_fetch_row(result); if (!row) break; table_info ti; ti.checked = false; ti.name = row[0]; tables_[ti.name] = ti; } } else { std::string errdesc = "failed to show tables mysql[dbname:"; errdesc += desc.dbname; errdesc += "]"; throw except(&mysql_, errdesc); } mysql_free_result(result); for(std::map >::iterator it = tables_.begin(); it != tables_.end(); ++it) { std::string desc_sql = "desc "; desc_sql += it->first; if (0 != mysql_query(&mysql_, desc_sql.c_str())) { std::string errdesc = "failed to desc mysql[tablename:"; errdesc += it->first; errdesc += "]"; throw except(&mysql_, errdesc); } result = mysql_store_result(&mysql_); if (result) { num_fields = mysql_num_fields(result); while(true) { MYSQL_ROW row = mysql_fetch_row(result); if (!row) break; it->second.fields.push_back(row[0]); } } mysql_free_result(result); } } template struct _check_table_field { struct check_field_name { check_field_name(const std::string & _name) : name(_name) { } const bool operator()(const std::string & _name)const { #ifdef WIN32 return 0 == ::stricmp(name.c_str(), _name.c_str()); #else return 0 == ::strcasecmp(name.c_str(), _name.c_str()); #endif } const std::string & name; }; _check_table_field(const table_info & _ti) :ti(_ti) { } template void operator()(const Field &) { std::string field_name = Field::name(); check_field_name cf(field_name); std::vector::const_iterator it = std::find_if(ti.fields.begin(), ti.fields.end(), cf); if (it == ti.fields.end()) { std::string errinfo = "object field["; errinfo += Field::name(); errinfo += "] not found in table"; throw std::logic_error(errinfo); } } const table_info & ti; }; template struct build_table_fields { build_table_fields(char * _sql, char * _key, char * _idx, char * _unique) : first(true) , sql(_sql) , key(_key) , idx(_idx) , unique(_unique) { } template void _set_str_size(int2type, const Field & fl) { if (Field::IsKey || Field::IsUnique || Field::IsIdx) sprintf(sql + ::strlen(sql), "(%u) binary", fl.max_size()); else sprintf(sql + ::strlen(sql), "(%u)", fl.max_size()); } template void _set_str_size(int2type, const Field &) { } template void operator()(const Field & fl) { if (first) first = false; else sprintf(sql + ::strlen(sql), ","); sprintf(sql + ::strlen(sql), "%s %s", Field::name(), type2sqlstring::type()); _set_str_size(int2type(), fl); if (Field::IsKey) { if (key[0] != 0) sprintf(key + ::strlen(key), ",%s", Field::name()); else sprintf(key + ::strlen(key), "%s", Field::name()); } else if (Field::IsUnique) { if (unique[0] != 0) sprintf(unique + ::strlen(unique), ", %s", Field::name()); else sprintf(unique + ::strlen(unique), "%s", Field::name()); } else if (Field::IsIdx) { if (idx[0] != 0) sprintf(idx + ::strlen(idx), ", %s", Field::name()); else sprintf(idx + ::strlen(idx), "%s", Field::name()); } } bool first; char * sql; char * key; char * idx; char * unique; }; template void build_create_table_sql(const Object & obj, char * sql) { sprintf(sql, "create table if not exists %s(", Object::name()); char keys[512]; keys[0] = 0; char idxs[512]; idxs[0] = 0; char unique[512]; unique[0] = 0; build_table_fields fields(sql, keys, idxs, unique); foreach((typename Object::tuple_type &)obj, fields); if (keys[0]) sprintf(sql + ::strlen(sql), ",primary key(%s)", keys); if (idxs[0]) { utilib::vectorN, 32> v; utilib::split(v, &idxs[0], &idxs[strlen(idxs)], ','); for(size_t i = 0; i < v.size(); ++i) { sprintf(sql + ::strlen(sql), ", index (%s)", v[i].c_str()); } } if (unique[0]) { utilib::vectorN, 32> v; utilib::split(v, &unique[0], &unique[strlen(unique)], ','); for(size_t i = 0; i < v.size(); ++i) { sprintf(sql + ::strlen(sql), ", unique (%s)", v[i].c_str()); } } sprintf(sql + ::strlen(sql), ")"); } template struct build_insert_fields { build_insert_fields(char * _sql) : first(true) , sql(_sql) { } template void operator()(const Field & fl) { if (fl.inited()) { if (first) first = false; else sprintf(sql + ::strlen(sql), ","); sprintf(sql + ::strlen(sql), "%s", Field::name()); } } bool first; char * sql; }; template struct build_insert_values { build_insert_values(char * _sql, MYSQL * _mysql) : sql(_sql) , mysql(_mysql) , first(true) { } template void _set_value(int2type, const Field & fl) { char buf[MAX_SQL_LEN]; to_string(fl.value, buf); sprintf(sql + ::strlen(sql), "'"); mysql_real_escape_string(mysql, sql + ::strlen(sql), buf, (unsigned long)::strlen(buf)); sprintf(sql + ::strlen(sql), "'"); } template void _set_value(int2type, const Field & fl) { to_string(fl.value, sql + ::strlen(sql)); } template void operator()(const Field & fl) { if (fl.inited()) { if (first) first = false; else sprintf(sql + ::strlen(sql), ","); _set_value(int2type(), fl); } } char * sql; MYSQL * mysql; bool first; }; template void build_insert_sql(const Object & obj, char * sql) { sprintf(sql, "insert into %s(", Object::name()); build_insert_fields fields(sql); foreach((typename Object::tuple_type &)obj, fields); sprintf(sql + ::strlen(sql), ") values("); build_insert_values values(sql, &mysql_); foreach((typename Object::tuple_type &)obj, values); sprintf(sql + ::strlen(sql), ")"); } template void build_replace_sql(const Object & obj, char * sql) { sprintf(sql, "replace into %s(", Object::name()); build_insert_fields fields(sql); foreach((typename Object::tuple_type &)obj, fields); sprintf(sql + ::strlen(sql), ") values("); build_insert_values values(sql, &mysql_); foreach((typename Object::tuple_type &)obj, values); sprintf(sql + ::strlen(sql), ")"); } template struct build_update_fields { build_update_fields(char * _sql, MYSQL * _mysql) :sql(_sql) ,mysql(_mysql) ,first(true) { } template void _set_value(int2type, const Field & fl) { char buf[MAX_SQL_LEN]; to_string(fl.value, buf); sprintf(sql + ::strlen(sql), "'"); mysql_real_escape_string(mysql, sql + ::strlen(sql), buf, (unsigned long)::strlen(buf)); sprintf(sql + ::strlen(sql), "'"); } template void _set_value(int2type, const Field & fl) { to_string(fl.value, sql + ::strlen(sql)); } template void operator()(const Field & fl) { if (fl.inited()) { if (first) first = false; else sprintf(sql + ::strlen(sql), ","); sprintf(sql + ::strlen(sql), "%s=", Field::name()); _set_value(int2type(), fl); } } char * sql; MYSQL * mysql; bool first; }; template struct build_where_fields { build_where_fields(char * _sql, MYSQL * _mysql) : sql(_sql) , mysql(_mysql) , first(true) { } void _set_value(int2type, const strict_base * st) { char buf[MAX_SQL_LEN]; unsigned c = st->to_string(buf); unsigned size = ::strlen(sql); sprintf(sql + size, buf); sprintf(sql + size + c, "'"); mysql_real_escape_string(mysql, sql + size + c + 1, buf + c, (unsigned long)::strlen(buf) - c); sprintf(sql + ::strlen(sql), "'"); } void _set_value(int2type, const strict_base * st) { st->to_string(sql + ::strlen(sql)); } template void operator()(const Field & fl) { const strict_base * st = fl.get_strict(); while(st) { if (first) first = false; else sprintf(sql + ::strlen(sql), " and "); sprintf(sql + ::strlen(sql), "%s", Field::name()); _set_value(int2type(), st); st = st->next(); } } char * sql; MYSQL * mysql; bool first; }; template void build_update_sql(const Object & obj, char * sql) { sprintf(sql, "update %s set ", Object::name()); build_update_fields fields(sql, &mysql_); foreach((typename Object::tuple_type &)obj, fields); sprintf(sql + ::strlen(sql), " where "); build_where_fields wf(sql, &mysql_); foreach((typename Object::tuple_type &)obj, wf); } template struct build_query_fields { build_query_fields(char * _sql) : first(true) , sql(_sql) { } template void operator()(const Field &) { if (first) first = false; else sprintf(sql + ::strlen(sql), ","); sprintf(sql + ::strlen(sql), "%s", Field::name()); } bool first; char * sql; }; template void build_query_sql(const Object & obj, char * sql) { sprintf(sql, "select "); build_query_fields fields(sql); foreach((typename Object::tuple_type &)obj, fields); sprintf(sql + ::strlen(sql), " from %s", Object::name()); char buf[MAX_SQL_LEN]; buf[0] = 0; build_where_fields wf(buf, &mysql_); foreach((typename Object::tuple_type &)obj, wf); if (buf[0]) { sprintf(sql + ::strlen(sql), " where %s", buf); } } template void build_remove_sql(const Object & obj, char * sql) { sprintf(sql, "delete from %s", Object::name()); char buf[MAX_SQL_LEN]; buf[0] = 0; build_where_fields wf(buf, &mysql_); foreach((typename Object::tuple_type &)obj, wf); if (buf[0]) { sprintf(sql + ::strlen(sql), " where %s", buf); } } MYSQL mysql_; //std::vector tables_; std::map > tables_; dbdesc desc_; }; // class database __SERVICE_SPACE_END_NS__ #endif