libsq3  2007.10.18
sq3_log_db.cpp
00001 
00002 #include "sq3_log_db.hpp"
00003 #include <iostream>
00004 #include <sstream>
00005 #include <vector>
00006 #include <cstdio> // vsnprint()
00007 #include <cstring>
00008 namespace sq3 {
00009 
00010     log_db::log_db( std::string const & filename )
00011         : database()
00012     {
00013         this->database::open(filename);
00014     }
00015 
00016     log_db::~log_db()
00017     {
00018     }
00019 
00020     int log_db::on_open()
00021     {
00022         if( ! this->is_open() )
00023         {
00024             return SQLITE_ERROR;
00025         }
00026         std::string sql( "create table if not exists log(ts,msg TEXT)" );
00027         this->execute( sql );
00028         /**
00029            enable temp_store=MEMORY to speed this up considably on PocketPC
00030            devices writing to SD cards.
00031         */
00032         this->pragma( "temp_store = MEMORY" );
00033         return SQLITE_OK;
00034     }
00035 
00036     int log_db::clear()
00037     {
00038         return this->execute( "delete from log" );
00039     }
00040 
00041     static char const * LOG_DB_LOG_INSERT_SQL = "insert into log (ts,msg) values(strftime('%Y-%m-%d %H:%M:%f','now'),?)";
00042         // "insert into log (ts,msg) values(current_timestamp,?)"
00043 
00044     bool log_db::log( std::string const & msg )
00045     {
00046         if( ! this->is_open() )
00047         {
00048             return false;
00049         }
00050         if( msg.empty() ) return true;
00051         statement st( *this, LOG_DB_LOG_INSERT_SQL );
00052         st.bind( 1, msg );
00053         int rc = st.execute();
00054         return rc_is_okay( rc );
00055         // In theory, if the count_changes PRAGMA is on then SQLITE_ROW will be returned from execute()
00056     }
00057 
00058     bool log_db::log(const char *format,...)
00059     {
00060         if( ! this->is_open() )
00061         {
00062             return false;
00063         }
00064         const int buffsz = static_cast<int>( std::max( (size_t) 2048, strlen(format) * 2 ) );
00065         std::vector<char> buffer( buffsz, '\0' );
00066         va_list vargs;
00067         va_start ( vargs, format );
00068         using namespace std;
00069         /** In gcc, vsnprintf() is in the std namespace, but in MSVC it is not, so we use 'using'
00070             to accomodate both cases. */
00071         int size = vsnprintf(&buffer[0], buffsz, format, vargs);
00072         va_end( vargs );
00073         if (size > (buffsz-1))
00074         {
00075             // replace tail of msg with "..."
00076             size = buffsz-1;
00077             for( int i = buffsz-4; i < buffsz-1; ++i )
00078             {
00079                 buffer[i] = '.';
00080             }
00081         }
00082         buffer[size] = '\0';
00083         if( size )
00084         {
00085             statement st( *this, LOG_DB_LOG_INSERT_SQL );
00086             st.bind( 1, &buffer[0], size );
00087             int rc = st.execute();
00088             //std::cout << "FYI: rc from an INSERT is " << rc << '\n'; // == SQLITE_DONE
00089             return SQLITE_DONE == rc;
00090         }
00091         return true;
00092     }
00093 
00094 #undef LOG_DB_LOG_INSERT_SQL
00095 
00096     void log_db::show_last( int count )
00097     {
00098         if( ! this->is_open() )
00099         {
00100             return;
00101         }
00102         std::ostream & os = std::cout;
00103         os << "sq3::log_db: most recent "
00104            << count << " entries:\n";
00105         if( ! this->is_open() )
00106         {
00107             os << "ERROR: Log database is not opened!";
00108             return;
00109         }
00110         std::ostringstream fmt;
00111         if( 0 )
00112         { // newest entries at the top:
00113         fmt << "select /*DATETIME(ts)*/ts,msg from log "
00114             << "order by ts desc, rowid desc"
00115             <<" limit " << count
00116             ;
00117         }
00118         else
00119         { // in "natural order":
00120             fmt << "select /*DATETIME(ts)*/ts,msg from log "
00121                 << "order by ts asc, rowid asc"
00122                 <<" limit " << count
00123                 ;
00124         }
00125         std::string sql(fmt.str());
00126         statement st( *this, sql );
00127         cursor r = st.get_cursor();
00128         std::string buff;
00129         while( SQLITE_ROW == r.step() )
00130         {
00131             std::string tmp;
00132             r.get( 0, tmp );
00133             os << tmp << ": ";
00134             r.get( 1, tmp );
00135             os << tmp << '\n';
00136         }
00137     }
00138 
00139     bool log_db::trim( int count )
00140     {
00141         if( this->is_open() )
00142         {
00143             std::ostringstream os;
00144             os << "delete from log where rowid not in (select rowid from log order by ts desc, rowid desc limit "<<count<<")";
00145             std::string sql( os.str() );
00146             if( SQLITE_OK == this->execute( sql.c_str() ) )
00147             {
00148                 this->vacuum();
00149             }
00150             return true; // delete will fail if the db is empty, but we'll consider that to be success
00151         }
00152         return false;
00153     }
00154 
00155 } // namespace