/* _______ .__ .__ .__ .__ \ \ ____ __ __ _______ |__|| | | | |__| ____ ____ / | \ _/ __ \ | | \\_ __ \| || | | | | | / _ \ / \ / | \\ ___/ | | / | | \/| || |__| |__| |( <_> )| | \ \____|__ / \___ >|____/ |__| |__||____/|____/|__| \____/ |___| / =========\/======\/=================================================\/== v0.01 04/JUL/2007 © Copyright 2007-2007 Scott D. Yelich SOME RIGHTS RESERVED .,-*~'`^`'~*-,._.,-*~'`^`'~*-,._.,-*~'`^`'~*-,._.-*~'`^`'~*-,._.,-*~'`^`'~*-,. LICENSE: Creative Commons Attribution 3.0 License. SEE: http://creativecommons.org/licenses/by/3.0/ Tue Aug 21 20:44:09 EDT 2007, v0.01 sdy Thu Aug 23 23:29:36 EDT 2007, v0.04 sdy This is the third version of simple class to facilitate working with sqlite3 from C++. The first verion used the convenience function sqlite3_get_table which kept the entire result set in memory. The second version used the legacy "exec" and a "two-step" double callback with the fisrt being a static member function receiving a passed in void* pointer that was static cast to Xql3 and then called a virtual callback. Wow, fugly. Buried in the docs, it states that use of "prepare" plus "step" is the new and preferred method of accessing the sqlite3 system, so although the legacy exec did just this, there were a few other legacy dependencies and if those were changed, it would probably break some code. Therefore, this class will implement an "exec" that is similar to the legacy exec. This exec will call a virtual callback that if not overridden, simply returns (ie: returned results are ignored). This version of the class handles multi-part SQL statements. */ // C++ // for error in dtor ... // #include // PACKAGE #include "Xql3.h" Xql3::Xql3() { // must do a manual call to Xql3::open(std::string) ... } Xql3::Xql3(std::string const & dbname) { open(dbname); } /* The destructor takes the necessary steps in order to close a database -- so that no data is lost or corrupted. The var _stmt was moved to a class var so that upon an exception and call to dtor, the _stmt could be finalized and the db could be closed. It's not necessary to call "close" in a catch -- although it won't hurt, if the exception is serious enough. */ Xql3::~Xql3() { int rc = 0; try { Xql3::close(); } catch (Xql3Exception & e) { // std::cerr << "Xql3Exception in Xql3::~Xql3() ..." << std::endl; } } int Xql3::open ( std::string const & name ) { _db_name = name.c_str(); int rc = 0; if (rc = sqlite3_open(_db_name, &_db)) { throw Xql3Exception(1, "open"); } /* // I decided that using the busy_handler allowed for more control. So // this is left commented out. if (rc = sqlite3_busy_timeout(_db, 25)) { // 25ms? throw Xql3Exception(2, "set busy timeout"); } */ if (rc = sqlite3_busy_handler(_db, Xql3::busy, this)) { throw Xql3Exception(3, "set busy handler"); } return rc; } /* close is called to make sure the database is closed so that no data is lost or corrupted. The var _stmt was moved to a class var so that upon an exception and call to dtor, the _stmt could be finalized and the db could be closed. It's not necessary to call "close" in a catch -- although it won't hurt, if the exception is serious enough. */ int Xql3::close ( ) { int rc = 0; if (_db) { if (_stmt) { rc = sqlite3_finalize(_stmt); } if (rc = sqlite3_close(_db) ) { throw Xql3Exception(4, "close"); } _db = 0; } return rc; } /* Ok, the idea here is that this callback is called for each line in the result of the SQL statement. The header is the names of the columns for the result and data holds the string representation of the value of the result for that column. This means that an int will be coerced into a string representation of the int -- and blobs have not been tested with this interface. A return value other than a 0 would cause the legacy exec to not continue with any more steps -- this version of Xql3 does not check the return value from the callback for any reason. */ int Xql3::callback(std::vector & headers, std::vector & data ) { int header_count = headers.size(); int i; if ( _headers.size() == 0 && header_count > 0 ) { for(i=0; i 0 ) { for(i=0; i headers; std::vector data; int i; int ncols; _stmt = 0; const char * zSql; const char * zLeftover = sql_text.c_str(); // clear out the old results ... _headers.clear(); _data.clear(); while (1) { // loop for all sql statements ... _stmt = 0; zSql = zLeftover; if (!zSql[0]) { break; } if ( SQLITE_OK != (rc = sqlite3_prepare(_db, zSql, -1, &_stmt, &zLeftover))) { throw Xql3Exception(5, "prepare"); } if (!_stmt) { // parsed blank or comment? continue; } ncols = sqlite3_column_count(_stmt); while (1) { // loop for all results of an individual statement ... rc = sqlite3_step(_stmt); if (SQLITE_ROW == rc) { if (0 == headers.size()) { for(i=0; i