WORM 0.2
A C++ DAL/ORM code generation framework
|
00001 /* 00002 WORM - a DAL/ORM code generation framework 00003 Copyright (C) 2011 Erik Winn <erikwinnmail@yahoo.com> 00004 00005 This program is free software: you can redistribute it and/or modify 00006 it under the terms of the GNU General Public License as published by 00007 the Free Software Foundation, either version 3 of the License, or 00008 (at your option) any later version. 00009 00010 This program is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 GNU General Public License for more details. 00014 00015 You should have received a copy of the GNU General Public License 00016 along with this program. If not, see <http://www.gnu.org/licenses/>. 00017 */ 00018 00019 #include "wmysqlresult.h" 00020 #include "wmysqldriver.h" 00021 00022 namespace WSql { 00023 00043 WMysqlDriver::WMysqlDriver( WSqlDatabase* db ): WSqlDriver( db ) 00044 { 00045 _mysql = 0; 00046 setIsOpen(false); 00047 //this _should_ make us thread safe .. 00048 int err =mysql_library_init(0, NULL, NULL); 00049 if (err) 00050 { 00051 std::string msg( "WMysqlDriver - WARNING! Error inilizing MySQL library!: " ); 00052 setError( WSqlError( msg, 99, WSqlError::DRIVER, WSqlError::FATAL ) ); 00053 setIsValid(false); 00054 }else 00055 init(); 00056 } 00057 00058 00059 WMysqlDriver::~WMysqlDriver() 00060 { 00061 close(); 00062 mysql_library_end(); 00063 } 00069 bool WMysqlDriver::init() 00070 { 00071 if ( isValid() ) 00072 return true; 00073 00074 //crude garbage collection: passing null tells library to free this after mysql_close() 00075 _mysql = mysql_init(NULL); 00076 if(NULL == _mysql) 00077 { 00078 setError( std::string( "WMysqlDriver: Memory allocation error!" ), WSqlError::DRIVER, 00079 WSqlError::FATAL, false ); 00080 return false; 00081 } 00082 setIsValid( true ); 00083 return true; 00084 } 00085 00086 00094 void WMysqlDriver::close() 00095 { 00096 if( !isValid() || !isOpen() ) 00097 return; 00098 mysql_close(_mysql); 00099 if(! _result->isCached() ) 00100 _result->setIsValid(false); 00101 setIsOpen(false); 00102 } 00103 00117 bool WMysqlDriver::open() 00118 { 00119 //this is equivilant to resetting if called more than once .. 00120 if ( isOpen() ) 00121 close(); 00122 if ( !init() ) 00123 return false; 00125 // - CLIENT_MULTI_STATEMENTS if we intend to CALL any procedures 00126 // but for now kiss, worm doesn't need much .. 00127 mysql_options(_mysql,MYSQL_READ_DEFAULT_GROUP,"worm"); 00128 MYSQL *retval = mysql_real_connect( _mysql, 00129 _database->hostName().empty()? NULL : _database->hostName().c_str(), 00130 _database->userName().empty()? NULL : _database->userName().c_str(), 00131 _database->password().empty()? NULL : _database->password().c_str(), 00132 _database->databaseName().empty()? NULL : _database->databaseName().c_str(), 00133 _database->port()<1? 0 : _database->port(), 00134 NULL, //socket 00135 0 // client flags .. 00136 ); 00137 if(NULL == retval) 00138 { 00139 std::string msg( "WMysqlDriver - Error opening database: " ); 00140 msg.append( mysql_error(_mysql) ); 00141 setError( WSqlError( msg, mysql_errno(_mysql), WSqlError::DRIVER, WSqlError::FATAL ) ); 00142 return false; 00143 } 00144 setIsValid(true); //just to be sure .. 00145 setIsOpen(true); 00146 return true; 00147 } 00157 bool WMysqlDriver::query(std::string sqlstring ) 00158 { 00159 if(!isOpen()) 00160 return false; 00161 00164 // for now it is disabled 00165 // FIXME std::string sql = local_escape_string(sqlstring); 00166 00167 std::string sql = sqlstring; 00168 if(sql.empty() ) 00169 setError("WARNING: WMysqlDriver string escape failed."); 00170 00171 if(mysql_real_query(_mysql, sql.c_str(),sql.length())) //should be zero 00172 { 00173 std::string msg( "WMysqlDriver - Error executing query: '" ); 00174 msg.append(sql + "', \n Server error: " + mysql_error(_mysql) ); 00175 setError( WSqlError( msg, mysql_errno(_mysql), WSqlError::QUERY, WSqlError::WARNING ) ); 00176 return false; 00177 } 00178 return true; 00179 } 00180 00213 WSqlResult* WMysqlDriver::result( bool iscached ) 00214 { 00215 delete _result; 00216 _result = new WMysqlResult(this); 00217 if(!iscached) 00218 std::cerr << "Warning: WMysqlDriver noncached results unimplemented!" << std::endl; 00219 //local result to avoid annoying static_cast .. 00220 MYSQL_RES *res = mysql_store_result(_mysql); 00221 if(NULL == res){ 00222 std::cout << "res is null: " << mysql_error(_mysql) << std::endl; 00223 int errno = mysql_errno(_mysql); 00224 if(errno){ 00225 std::string msg = "WMysqlDriver: Error fetching result set. \n Server error: "; 00226 msg.append(mysql_error(_mysql)); 00227 setError( WSqlError( msg, errno, WSqlError::QUERY, WSqlError::WARNING ) ); 00228 _result->setIsValid(false); 00229 } 00230 }else{ 00231 MYSQL_ROW row; 00232 ulong numfields = mysql_num_fields(res); 00233 while((row = mysql_fetch_row(res))) 00234 { 00235 ulong *lengths = mysql_fetch_lengths(res); 00236 WSqlRecord record; 00237 for(ulong fld_pos=0; fld_pos < numfields; ++ fld_pos) 00238 { 00239 MYSQL_FIELD *m_field = mysql_fetch_field_direct(res, fld_pos); 00240 WSqlField fld; 00241 fld.setName(m_field->name); 00242 fld.setColumnName(m_field->org_name); 00243 if(0 == lengths[fld_pos]) 00244 if(NULL == lengths[fld_pos]) 00245 fld.setData(std::string("NULL")); 00246 else 00247 fld.setData(std::string("")); 00248 else 00249 fld.setData(row[fld_pos]); 00250 record.append(fld); 00251 } 00252 _result->addRecord(record); 00253 } 00254 //note that the MYSQL_RES is stored in our result, not freed here.. 00255 static_cast<WMysqlResult *>(_result)->setResult(res); 00256 _result->setIsValid(true); 00257 } 00258 return _result; 00259 } 00260 00273 std::vector< std::string > WMysqlDriver::tableNames() 00274 { 00275 std::vector<std::string>vecToReturn; 00276 if(!isOpen()) { 00277 setError("tableNames: Connection not open - use open()"); 00278 return vecToReturn; 00279 } 00280 if(_database->databaseName().empty()) { 00281 setError("tableNames: You must set a database name first!"); 00282 return vecToReturn; 00283 } 00284 std::string sql = "show tables"; 00285 query(sql); 00286 result(); 00287 int numtables = _result->count(); 00288 for(int i=0;i<numtables;++i) 00289 vecToReturn.push_back(_result->fetch(i).field(0).data<std::string>()); 00290 return vecToReturn; 00291 } 00333 WSqlTable WMysqlDriver::tableMetaData( const std::string& tableName ) 00334 { 00335 WSqlTable tblToReturn; 00336 if ( !_tables.empty() ) 00337 { 00338 tblToReturn = findTable( tableName ); 00339 if ( tblToReturn.isValid() ) 00340 return tblToReturn; 00341 } 00342 tblToReturn.setName(tableName); 00343 00344 std::vector<std::string> column_names; 00345 std::vector<std::string>::const_iterator column_names_it; 00346 std::string sql("show columns in "); sql.append(tableName); 00347 query(sql); 00348 result(); 00349 WSqlColumn clm; 00350 WSqlRecord record = _result->fetchFirst(); 00351 while(!record.empty()) 00352 { 00361 clm.setColumnName(record.field(0).data<std::string>()); 00362 column_names.push_back(clm.columnName()); 00363 initColumnType(clm, record.field(1).data<std::string>()); 00364 bool nullable = (record.field(2).data<std::string>().compare("NO") == 0); 00365 bool primarykey = (record.field(3).data<std::string>().compare("PRI") == 0); 00366 clm.setDefaultValue( record.field(4).data<std::string>()); 00367 bool isauto = (record.field(5).data<std::string>().find("auto_increment") != std::string::npos); 00368 clm.setCanBeNull(nullable); 00369 clm.setIsPrimaryKey(primarykey); 00370 clm.setIsAutoIncremented(isauto); 00371 tblToReturn.append(clm); 00372 record = _result->fetchNext(); 00373 } 00374 //initialize foreign keys .. 00375 WSqlForeignKey fk; 00376 for(column_names_it = column_names.begin();column_names_it != column_names.end(); column_names_it++) 00377 { 00378 00379 sql = "select constraint_name, referenced_table_schema, referenced_table_name," 00380 " referenced_column_name from information_schema.key_column_usage " 00381 " where table_name like '" + tableName + "' and column_name like '" + *column_names_it + "'"; 00382 00383 query(sql); 00384 result(); 00385 WSqlRecord record = _result->fetchFirst(); 00386 while(!record.empty()) 00387 { 00388 if( record["referenced_column_name"].compare("NULL") == 0) 00389 { 00390 record = _result->fetchNext(); 00391 continue; 00392 } 00393 fk.setColumnName(*column_names_it); 00394 fk.setTableName(tblToReturn.name()); 00395 fk.setKeyName(record["constraint_name"]); 00396 fk.setReferencedColumnName(record["referenced_column_name"]); 00397 fk.setReferencedSchemaName(record["referenced_schema_name"]); 00398 fk.setReferencedTableName(record["referenced_table_name"]); 00399 tblToReturn.addForeignKey(fk); 00400 record = _result->fetchNext(); 00401 } 00402 } 00403 tblToReturn.setIsValid(true); 00404 _tables.push_back(tblToReturn); 00405 return tblToReturn; 00406 } 00407 00408 bool WMysqlDriver::isOpen() 00409 { 00410 return isValid() && WSqlDriver::isOpen(); 00411 } 00412 00414 void WMysqlDriver::initColumnType( WSqlColumn &clm, std::string description ) 00415 { 00416 std::string size; 00417 std::string tname; 00418 size_t pos = description.find("unsigned"); 00419 if ( pos != std::string::npos) 00420 { 00421 clm.setIsUnsigned(true); 00422 description.erase(pos, 8); 00423 } 00424 pos = description.find_first_of("("); 00425 if(pos != std::string::npos) 00426 { 00427 size_t endpos = description.find_last_of(")"); 00428 size = description.substr(pos+1, endpos - (pos+1)); 00429 clm.setMaxLength(atoi(size.c_str())); 00430 description.erase(pos, endpos - pos ); 00431 }else{ 00432 pos = description.find_first_of(" "); 00433 } 00434 if(pos != std::string::npos) 00435 tname = description.substr(0,pos); 00436 else 00437 tname = description; 00438 clm.setDataType(WSqlDataType::toType(tname)); 00439 } 00440 00442 std::string WMysqlDriver::local_escape_string(std::string& escapeme) 00443 { 00444 if(escapeme.empty()) 00445 return escapeme; 00446 ulong inlength = escapeme.length(); 00447 char *buffer = new char[(inlength * 2) + 1]; 00448 if(NULL == buffer) { 00449 setError("Warning: escape string memory allocation error! String not escaped."); 00450 return std::string(); 00451 } 00452 ulong retlen = mysql_real_escape_string(_mysql, buffer, escapeme.c_str(), inlength); 00453 if(retlen < inlength){ 00454 setError("Warning: escape string size error! String not escaped."); 00455 delete[] buffer; 00456 return escapeme; 00457 } 00458 std::string ret = buffer; 00459 delete[] buffer; 00460 return ret; 00461 } 00462 00463 }//namespace WSql