WORM 0.2
A C++ DAL/ORM code generation framework

src/sql/drivers/wmysqldriver.cpp

Go to the documentation of this file.
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
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Defines