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

src/sql/drivers/wsqlitedriver.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 "wsqliteresult.h"
00020 #include "wsqlitedriver.h"
00021 #include "wsqlforeignkey.h"
00022 
00023 #include <iostream>
00024 #include <boost/algorithm/string.hpp>
00025 
00026 #define ROWENDTAG "-*-"
00027 
00028 namespace WSql
00029 {
00030 
00066 bool isForeignKeyDefinition(std::string sql)
00067 {
00068     boost::to_upper( sql );
00069     return( sql.find("FOREIGN KEY") != std::string::npos );
00070 }
00072 bool isIndexDefinition(std::string sql)
00073 {
00074     boost::to_upper( sql );
00075     return (boost::istarts_with(sql,"UNIQUE")
00076     ||boost::istarts_with(sql,"PRIMARY KEY")
00077     ||boost::istarts_with(sql,"INDEX")    
00078     ||boost::istarts_with(sql,"CREATE INDEX")
00079     ||boost::istarts_with(sql,"CREATE PRIMARY KEY")
00080     ||boost::istarts_with(sql,"CREATE UNIQUE")
00081     ||boost::istarts_with(sql,"CONSTRAINT INDEX")
00082     ||boost::istarts_with(sql,"CONSTRAINT UNIQUE")
00083     ||boost::istarts_with(sql,"CONSTRAINT PRIMARY KEY")
00084     );
00085 }
00086 
00088 WSqlForeignKey createForeignKey(std::string sqldef)
00089 {
00090     WSqlForeignKey fkToReturn;
00091     size_t sz = sqldef.size();
00092     size_t pos = 0;
00093     while(pos < sz) //sqlite is case insensitive ..
00094         sqldef[pos] = tolower(sqldef[pos++]);
00095 //    std::cerr << "FK def: *" << sqldef << "*" << std::endl;
00096     //constraint "fk_comment_post" foreign key ("post_id") references "post" ("id")
00097     boost::trim_if(sqldef, boost::is_any_of("\n\t "));
00098     
00099     size_t start = sqldef.find_first_of("(");
00100     size_t end = sqldef.find_first_of(")", start);
00101     if(std::string::npos != start && std::string::npos != end )
00102     {
00103         std::string tmp = sqldef.substr(start+1, (end - start)-1);
00104         boost::trim_if( tmp, boost::is_any_of(" '\""));
00105 //        std::cerr << " column: trimmed *" << tmp << "*" <<  std::endl;
00106         start = sqldef.find_first_of("(",end);
00107         end = sqldef.find_first_of(")", start);
00108         fkToReturn.setColumnName(tmp);
00109         if(std::string::npos != start && std::string::npos != end )
00110         {
00111             std::string tmp2 = sqldef.substr(start+1, (end - start) -1);
00112             boost::trim_if(tmp2, boost::is_any_of(" '\""));
00113 //            std::cerr << "target column: trimmed *" << tmp2 << "*" <<  std::endl;
00114             fkToReturn.setReferencedColumnName(tmp2);
00115         }
00116     }else
00117         return fkToReturn;
00118     
00119     start = sqldef.find("references");
00120     if(std::string::npos != start)
00121     {
00122         start += 10;
00123         end = sqldef.find("(",start);
00124         std::string tmp3 = sqldef.substr(start, (end-start)-1);
00125         boost::trim_if(tmp3, boost::is_any_of(" '\""));
00126 //        std::cerr << "== references table: trimmed *" << tmp3 << "*" <<  std::endl;
00127         fkToReturn.setReferencedTableName(tmp3);
00128     }else
00129         return fkToReturn;
00130     start = sqldef.find("constraint");
00131     if(std::string::npos != start)
00132     {
00133         start = sqldef.find_first_of("'\"");
00134         end = sqldef.find_first_of("'\"",start+1);
00135         std::string tmp4 = sqldef.substr(start, (end-start)-1);
00136         boost::trim_if(tmp4, boost::is_any_of(" '\""));
00137 //        std::cerr << "constraint name: trimmed *" << tmp4 << "*" <<  std::endl;
00138         fkToReturn.setKeyName(tmp4);
00139     }    
00140     return fkToReturn;
00141 }
00142 
00143 //ugly brute force, don't look ..
00145 void splitIntoDefinitions(std::vector<std::string> &vecToFill, std::string sql)
00146 {
00147     size_t pos = 0;
00148     size_t max = sql.size();
00149     while(pos < max )
00150     {
00151         if(sql[pos] == '(')
00152             while(++pos < max && sql[pos] != ')')
00153                 continue;
00154         if(sql[pos] == ',')
00155         {
00156             std::string part =sql.substr(0,pos);
00157             boost::trim(part);
00158             vecToFill.push_back(part);
00159             sql.erase(0,pos+1);
00160             boost::trim(sql);
00161             max = sql.size();
00162             pos=0;
00163         }else
00164             pos++;
00165     }
00166     if(!sql.empty())
00167         vecToFill.push_back(sql);
00168 }
00172 WSqlDataType::Type WSqliteDriver::sqlite3TypeToWSqlType( std::string tname )const
00173 {
00174     boost::to_lower( tname );
00175 
00176     //note - this includes "bigint"; that would be stored as an "integer" in sqlite
00177     if(tname.find( "int" ) != std::string::npos)
00178         return WSqlDataType::INT;
00179     if(tname.find( "text" ) != std::string::npos)
00180         return WSqlDataType::TEXT;
00181     if(tname.find( "char" ) != std::string::npos)
00182         return WSqlDataType::TEXT;
00183     if(tname.find( "clob" ) != std::string::npos)
00184         return WSqlDataType::TEXT;
00185     if(tname.find( "blob" ) != std::string::npos)
00186         return WSqlDataType::BLOB;
00187     if(tname.find( "floa" ) != std::string::npos)
00188         return WSqlDataType::DECIMAL;
00189     if(tname.find( "real" ) != std::string::npos)
00190         return WSqlDataType::DOUBLE;
00191     if(tname.find( "doub" ) != std::string::npos)
00192         return WSqlDataType::DOUBLE;
00193     return WSqlDataType::DECIMAL; //sqlite's "NUMERIC", the default ..
00194 }
00195 
00196 WSqlDataType::Type WSqliteDriver::sqlite3TypeToWSqlType( int dtype )const
00197 {
00198     switch ( dtype ) {
00199         case SQLITE_INTEGER:
00200             return WSqlDataType::INT;
00201         case SQLITE_FLOAT:
00202             return WSqlDataType::DECIMAL;
00203         case SQLITE_TEXT:
00204             return WSqlDataType::TEXT;
00205         case SQLITE_BLOB:
00206             return WSqlDataType::BLOB;
00207         default:
00208             return WSqlDataType::NOTYPE;
00209     }
00210 }
00211 
00212 WSqliteDriver::WSqliteDriver( WSqlDatabase* db ) : WSqlDriver( db )
00213 {
00214     _isUtf8 = true; //todo:support utf16
00215 }
00216 
00217 WSqliteDriver::~WSqliteDriver()
00218 {
00219     close();
00220 }
00221 
00222 bool WSqliteDriver::init()
00223 {
00224     if ( isValid() ) return true;
00225 
00226     if ( !_database ) {
00227         setError( std::string( "WSqliteDriver: No database object set!" ), WSqlError::DRIVER,
00228                   WSqlError::FATAL, false );
00229         return false;
00230     }
00232     if (_database->databaseName().empty() ) {
00233         setError( std::string( "WSqliteDriver: No database specified!" ), WSqlError::DRIVER,
00234                   WSqlError::FATAL, false );
00235         return false;
00236     }
00237     setIsValid( true );
00238     return true;
00239 }
00240 
00241 bool WSqliteDriver::open()
00242 {
00243     if ( isOpen() ) return true;
00244     if ( !init() ) return false;
00245 
00246     int err = sqlite3_open( _database->databaseName().c_str(), &_objSqlite );
00247     if ( SQLITE_OK != err ) 
00248     {
00249         std::string msg( "WSqliteDriver - Error opening database: " );
00250         msg.append( sqlite3_errmsg( _objSqlite ) );
00251         setError( WSqlError( msg, err, WSqlError::DRIVER, WSqlError::FATAL ) );
00252         setHasError( true );
00253         setIsValid( false );
00254         return false;
00255     }
00256     setHasError( false );
00257     setIsOpen( true );
00258     setIsValid( true );
00259     return true;
00260 }
00261 
00262 void WSqliteDriver::close()
00263 {
00264     if ( isValid() )
00265         sqlite3_close( _objSqlite );
00266     _columns_map.clear();
00267     _foreign_keys.clear();
00268     setIsOpen( false );
00269     setIsValid(false);
00270 }
00271 
00272 //WSqlResult WSqliteDriver::exec(const WSqlQuery &queryObject){}
00273 
00292 bool WSqliteDriver::query(std::string sql)
00293 {
00294     if ( !isOpen() ) {
00295         setError( std::string( "WSqliteDriver - connection not open! Call open() first. " ),
00296                   WSqlError::DRIVER, WSqlError::FATAL, false );
00297         return false;
00298     }
00299     WSqliteResult *result = new WSqliteResult( this );
00300     if ( 0 == result ) {
00301         setError( std::string( "WSqliteDriver - memory allocation error! Failed to create WSqlResult! " ),
00302                   WSqlError::DRIVER, WSqlError::FATAL, false );
00303         return false;
00304     }
00305     result->setIsCached( true );
00306     sqlite3_stmt *statement;
00307     int err = sqlite3_prepare_v2( _objSqlite, sql.c_str(), -1, &statement, 0 );
00308     if ( SQLITE_OK == err ) 
00309     {
00310         int cols = sqlite3_column_count( statement );
00311         while ( sqlite3_step( statement ) == SQLITE_ROW ) 
00312         {
00313             WSqlRecord record;
00314             for ( int col = 0; col < cols; col++ ) 
00315             {
00316                 WSqlField fld;
00317                 std::string text;
00318                 std::string colname;
00319                 std::string fldname;
00320                 const unsigned char * safety = sqlite3_column_text( statement, col );
00321                 if ( safety ) text = ( char * ) safety;
00322                 const char * safety2 = sqlite3_column_origin_name( statement, col );
00323                 if ( safety ) colname = ( char * ) safety2;
00324                 const char * safety3 = sqlite3_column_name( statement, col );
00325                 if ( safety ) fldname = ( char * ) safety3;
00326 
00327                 fld.setData( text );
00328                 if ( !fldname.empty() )
00329                     fld.setName( fldname );
00330                 else
00331                     fld.setName( colname );
00332                 fld.setColumnName( colname );
00333 
00334                 record.append( fld );
00335             }
00336             result->addRecord( record );
00337         }
00338         delete _result;
00339         _result = result;
00340         sqlite3_finalize( statement );
00341         return true;
00342     }else {
00343         std::string msg( "WSqliteDriver - query failure, SQL: " + sql + "\n Server said: " );
00344         msg.append( sqlite3_errmsg( _objSqlite ) );
00345         setError( WSqlError( msg, err, WSqlError::QUERY, WSqlError::DANGER ) );
00346         setHasError( true );
00347         setIsValid( false );
00348         return false;
00349     }
00350 }
00351 
00352 WSqlResult* WSqliteDriver::result(bool iscached)
00353 {
00354     if ( 0 == _result )
00355         _result = new WSqliteResult( this );
00356     return _result;
00357 }
00358 
00360 std::vector<std::string> WSqliteDriver::tableNames()
00361 {
00362     std::vector<std::string> vecToReturn;
00363     if ( !isOpen() )
00364         return vecToReturn;
00365     sqlite3_stmt *statement;
00366     const char *sql = "SELECT name from sqlite_master WHERE type='table'";
00367     if ( sqlite3_prepare_v2( _objSqlite, sql, -1, &statement, 0 ) == SQLITE_OK ) 
00368     {
00369         while ( sqlite3_step( statement ) == SQLITE_ROW ) 
00370         {
00371             const unsigned char * safety = sqlite3_column_text( statement, 0 ); 
00372             std::string tname;
00373             if(NULL != safety)
00374                 tname = ( char* )safety; 
00375             if (tname.empty() || tname.compare( "sqlite_sequence" ) == 0 )
00376                 continue;
00377             vecToReturn.push_back(tname);
00378         }
00379         sqlite3_finalize( statement );
00380     }
00381     return vecToReturn;
00382 }
00383 
00384 WSqlTable WSqliteDriver::tableMetaData( const std::string &tableName )
00385 {
00386     WSqlTable tblToReturn;
00387     if ( !_tables.empty() ) 
00388     {
00389         tblToReturn = findTable( tableName );
00390         if ( tblToReturn.isValid() )
00391             return tblToReturn;
00392     }
00393     tblToReturn.setName( tableName );
00394     if ( !isOpen() )//huh? .. nevermind..todo: something nicer
00395         return tblToReturn;   
00396     //parse and store info from the create statement ..
00397     std::string sqlToParse = fetchTableCreateStatement( tableName );
00398     parseSchema(sqlToParse);
00399     
00400     sqlite3_stmt *statement;
00401     std::string col_descript;
00402     std::string sql( "pragma table_info(" );
00403     sql.append( tableName );
00404     sql.append( ");" );
00405     if ( sqlite3_prepare_v2( _objSqlite, sql.c_str(), -1, &statement, 0 ) == SQLITE_OK ) 
00406     {
00407         int numcols = sqlite3_column_count( statement );
00408         while ( sqlite3_step( statement ) == SQLITE_ROW ) 
00409         {
00410             WSqlColumn clm;
00411             for ( int i = 0; i < numcols; i++ ) 
00412             {
00413                 const unsigned char * safety = sqlite3_column_text( statement, i );
00414                 if ( !safety )
00415                     continue;
00416                 col_descript = ( char * ) safety;
00417                 switch ( i ) {
00418                     case 1:
00419                         clm.setColumnName( col_descript );
00420                         break;
00421                     case 2:
00422                         clm.setDataType( sqlite3TypeToWSqlType( col_descript ) );
00423                         break;
00424                     case 3:
00425                         clm.setCanBeNull( col_descript.compare( "0" ) ? false : true ); //careful..
00426                         break;
00427                     case 4:
00428                         clm.setDefaultValue( col_descript );
00429                         break;
00430                     case 5:
00431                         clm.setIsPrimaryKey( col_descript.compare( "0" ) ? true : false );
00432                         break;
00433                     default:
00434                         continue;
00435                 }
00436             }
00437             clm.setIsAutoIncremented(columnIsAutoIncrement(clm.columnName()));
00438             tblToReturn.append( clm );
00439         }
00440         sqlite3_finalize( statement );
00441     }    
00442     tblToReturn.setIsValid(true);
00443     std::vector<WSqlForeignKey>::iterator it = _foreign_keys.begin();
00444     for(; it != _foreign_keys.end(); ++it)
00445     {
00446        it->setTableName(tableName);
00447         tblToReturn.addForeignKey(*it);
00448     }
00449     _tables.push_back( tblToReturn );
00450     _columns_map.clear();
00451     _foreign_keys.clear();
00452     return tblToReturn;
00453 }
00454 
00456 bool WSqliteDriver::columnIsAutoIncrement( const std::string& columnname )const
00457 {
00459     
00460     std::map<std::string, std::string>::const_iterator it = _columns_map.find(columnname);
00461     if(_columns_map.end() == it)
00462         return false;
00463     std::string col_definition = it->second;
00464     boost::to_lower(col_definition);
00465     if(col_definition.find("autoincrement") != std::string::npos
00466         || col_definition.find("auto_increment") != std::string::npos
00467         )
00468         return true;
00469     return false;
00470 }
00471 
00473 std::string WSqliteDriver::fetchTableCreateStatement( const std::string& tablename )const
00474 {
00475     sqlite3_stmt *statement;
00476     std::string sqlToReturn;
00477     std::string sql( "SELECT sql from sqlite_master WHERE tbl_name = '" );
00478     sql.append( tablename ); sql.append( "';" );
00479 
00480     if ( sqlite3_prepare_v2( _objSqlite, sql.c_str(), -1, &statement, 0 ) == SQLITE_OK ) 
00481     {
00482         while ( sqlite3_step( statement ) == SQLITE_ROW ) 
00483         {
00484             const unsigned char * safety = sqlite3_column_text( statement, 0 );
00485             if ( safety && SQLITE_ERROR != *safety ) {
00486                 sqlToReturn.append(( char* ) safety );
00487                 sqlToReturn.append( ROWENDTAG );
00488             }
00489             else
00490             {//this is quite weird - empty rows .. 
00491                 std::cerr << "fetchTableCreateStatement - sqlite weirdness: " << tablename << std::endl;
00492                 //std::cerr <<  sqlite3_errmsg( _objSqlite ) << std::endl;
00493             }
00494             //??usleep();
00495         }
00496         sqlite3_finalize( statement );
00497     }
00498     return sqlToReturn;
00499 }
00500 
00502 std::string WSqliteDriver::extractStatement( const std::string& sqlToParse, const char start, const char end ) const
00503 {
00504     std::string sqlToReturn;
00505     size_t pos_start = 0; size_t pos_end = 0;
00506     pos_start = sqlToParse.find_first_of( start );
00507     pos_end = sqlToParse.find_last_of( end );
00508     if ( std::string::npos == pos_start || std::string::npos == pos_end )
00509         return sqlToReturn;
00510     sqlToReturn = sqlToParse.substr( pos_start+1, pos_end - (pos_start+1));
00511 //    std::cerr << "extract Statement sqlToReturn: " << sqlToReturn << std::endl;
00512     return sqlToReturn;
00513 }
00514 
00515 //eh, not graceful but mostly works
00517 void WSqliteDriver::parseSchema( std::string& sql)
00518 {
00519     typedef std::vector<std::string>::iterator Iter;
00520     std::vector<std::string> statements;  //complete statements in parens
00521     std::vector<std::string> definitions;  //comma separated column defs
00522     
00523     std::vector<std::string> columns;      //stored split off columns, constraints, indices
00524     std::vector<std::string> indices;
00525         
00526     boost::trim_if(sql, boost::is_any_of("-*"));
00527     boost::split( statements, sql,  boost::is_any_of( "-*" ), boost::token_compress_on );    
00528     Iter stmt_it = statements.begin();
00529     for ( ;stmt_it != statements.end();++stmt_it ) 
00530     {
00531         std::string cur_statement =  *stmt_it;
00532         boost::trim(cur_statement);
00533         //get statement between parens - but only for the first "CREATE"; 
00534         //thus avoid extracting primary key definitions like (x_id,y_id)
00535         if(boost::istarts_with(cur_statement,"CREATE TABLE"))
00536             cur_statement = extractStatement(cur_statement);
00537         //cut up by line -comma separated column definitions
00538         splitIntoDefinitions(definitions, cur_statement);        
00539         Iter defs_it = definitions.begin();
00540         for ( ; defs_it != definitions.end();++defs_it ) 
00541         {
00542             std::string cur_definition = *defs_it;
00543             boost::trim(cur_definition);
00544             if( isForeignKeyDefinition(cur_definition))
00545             {
00546 /*                std::cout << "cur_definition: " << cur_definition << std::endl 
00547                     << "************** FOREIGN KEY ***************" << std::endl;*/
00548                 WSqlForeignKey fk = createForeignKey(cur_definition);
00549                 _foreign_keys.push_back(fk);
00550             }
00551             else if( isIndexDefinition(cur_definition) )
00552             {
00553 /*                std::cout << "cur_definition: " << cur_definition << std::endl
00554                 << "************** INDEX ***************" << std::endl;*/
00555                 indices.push_back(cur_definition);
00556             }
00557             else
00558                 columns.push_back(cur_definition);
00559         }
00560         definitions.clear();
00561     }
00562     //map to local map 
00563     mapColumns(columns);
00564 }
00565 
00567 void WSqliteDriver::mapColumns(std::vector<std::string> &vecColumnDefinitions)
00568 {
00569     std::vector<std::string>::const_iterator it = vecColumnDefinitions.begin();
00570     for(;it != vecColumnDefinitions.end();++it)
00571     {
00572         std::string cur_definition = *it;
00573         size_t pos = cur_definition.find_first_of("\t ");
00574         if(pos != std::string::npos)
00575         {
00576             std::string cur_colname = cur_definition.substr(0,pos);
00577             boost::trim_if(cur_colname, boost::is_any_of("\n\t []`'\""));
00578             _columns_map[cur_colname] = cur_definition.erase(0,pos+1); 
00579         }        
00580     }
00581 }
00582 }//namespace WSql
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Defines