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 "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