#include "sqlitedb.h" #include "sqlbrowser_util.h" #include #include #include #include #include #include void DBBrowserTable::addField(int order, const QString& wfield,const QString& wtype) { fldmap[order] = DBBrowserField(wfield,wtype); } bool DBBrowserDB::isOpen ( ) { return _db!=0; } void DBBrowserDB::setDirty(bool dirtyval) { if ((dirty==false)&&(dirtyval==true)) { setRestorePoint(); } dirty = dirtyval; if (logWin) { logWin->msgDBDirtyState(dirty); } } void DBBrowserDB::setDirtyDirect(bool dirtyval) { dirty = dirtyval; if (logWin) { logWin->msgDBDirtyState(dirty); } } bool DBBrowserDB::getDirty() { return dirty; } void DBBrowserDB::setEncoding( int encoding ) { curEncoding = encoding; } void DBBrowserDB::setDefaultNewData( const QString & data ) { curNewData = data; } char * DBBrowserDB::GetEncodedQStringAsPointer( const QString & input) { if (curEncoding==kEncodingUTF8) return (char *) input.toUtf8().constData(); if (curEncoding==kEncodingLatin1) return (char *) input.toLatin1().constData(); return (char *) input.constData(); } QString DBBrowserDB::GetEncodedQString( const QString & input) { if (curEncoding==kEncodingUTF8) return input.toUtf8(); if (curEncoding==kEncodingLatin1) return input.toLatin1(); return input; } QString DBBrowserDB::GetDecodedQString( const QString & input) { // if (curEncoding==kEncodingUTF8) return QString::fromUtf8(input); // if (curEncoding==kEncodingLatin1) return QString::fromLatin1(input); return input; } bool DBBrowserDB::open ( const QString & db) { bool ok=false; int err; if (isOpen()) close(); //try to verify the SQLite version 3 file header QFile dbfile(db); if ( dbfile.open( QIODevice::ReadOnly ) ) { char buffer[16+1]; dbfile.readLine(buffer, 16); QString contents = QString(buffer); dbfile.close(); if (!contents.startsWith("SQLite format 3")) { lastErrorMessage = QString("File is not a SQLite 3 database"); return false; } } else { lastErrorMessage = QString("File could not be read"); return false; } lastErrorMessage = QString("no error"); err = sqlite3_open_v2(db.toUtf8(), &_db, SQLITE_OPEN_READWRITE, NULL); if ( err ) { lastErrorMessage = sqlite3_errmsg(_db); sqlite3_close(_db); _db = 0; return false; } if (_db){ if (SQLITE_OK==sqlite3_exec(_db,"PRAGMA empty_result_callbacks = ON;", NULL,NULL,NULL)){ if (SQLITE_OK==sqlite3_exec(_db,"PRAGMA show_datatypes = ON;", NULL,NULL,NULL)){ ok=true; setDirty(false); } curDBFilename = db; } } return ok; } bool DBBrowserDB::setRestorePoint() { if (!isOpen()) return false; if (_db){ sqlite3_exec(_db,"SAVEPOINT RESTOREPOINT;", NULL,NULL,NULL); setDirty(false); } return true; } bool DBBrowserDB::save() { if (!isOpen()) return false; if (_db){ sqlite3_exec(_db,"RELEASE RESTOREPOINT;", NULL,NULL,NULL); setDirty(false); } return true; } bool DBBrowserDB::revert() { if (!isOpen()) return false; if (_db){ sqlite3_exec(_db,"ROLLBACK TO SAVEPOINT RESTOREPOINT;", NULL,NULL,NULL); setDirty(false); } return true; } bool DBBrowserDB::create ( const QString & db) { bool ok=false; if (isOpen()) close(); lastErrorMessage = QString("no error"); if( sqlite3_open(db.toUtf8(), &_db) != SQLITE_OK ){ lastErrorMessage = sqlite3_errmsg(_db); sqlite3_close(_db); _db = 0; return false; } if (_db){ if (SQLITE_OK==sqlite3_exec(_db,"PRAGMA empty_result_callbacks = ON;", NULL,NULL,NULL)){ if (SQLITE_OK==sqlite3_exec(_db,"PRAGMA show_datatypes = ON;", NULL,NULL,NULL)){ ok=true; setDirty(false); } curDBFilename = db; } } return ok; } void DBBrowserDB::close (){ if (_db) { if (getDirty()) { QString msg = "Do you want to save the changes made to the database file "; msg.append(curDBFilename); msg.append(" ?"); if (QMessageBox::question( 0, applicationName ,msg, QMessageBox::Yes, QMessageBox::No)==QMessageBox::Yes) { save(); } else { //not really necessary, I think... but will not hurt. revert(); } } sqlite3_close(_db); } _db = 0; idxmap.clear(); tbmap.clear(); idmap.clear(); browseRecs.clear(); browseFields.clear(); hasValidBrowseSet = false; } bool DBBrowserDB::compact ( ) { char *errmsg; bool ok=false; if (!isOpen()) return false; if (_db){ save(); logSQL(QString("VACUUM;"), kLogMsg_App); if (SQLITE_OK==sqlite3_exec(_db,"VACUUM;", NULL,NULL,&errmsg)){ ok=true; setDirty(false); } } if (!ok){ lastErrorMessage = QString(errmsg); return false; }else{ return true; } } bool DBBrowserDB::reload( const QString & filename, int * lineErr) { /*to avoid a nested transaction error*/ sqlite3_exec(_db,"COMMIT;", NULL,NULL,NULL); FILE * cfile = fopen(filename.toUtf8(), (const char *) "r"); load_database(_db, cfile, lineErr); fclose(cfile); setDirty(false); if ((*lineErr)!=0) { return false; } return true; } bool DBBrowserDB::dump( const QString & filename) { FILE * cfile = fopen(filename.toUtf8(), (const char *) "w"); if (!cfile) { return false; } dump_database(_db, cfile); fclose(cfile); return true; } bool DBBrowserDB::executeSQL ( const QString & statement, bool dirtyDB, bool logsql) { char *errmsg; bool ok = false; if (!isOpen()) return false; if (_db){ if (logsql) logSQL(statement, kLogMsg_App); if (dirtyDB) setDirty(true); if (SQLITE_OK==sqlite3_exec(_db,statement.toUtf8(), NULL,NULL,&errmsg)){ ok=true; } } if (!ok){ lastErrorMessage = QString(errmsg); return false; } return true; } bool DBBrowserDB::addRecord ( ) { char *errmsg; if (!hasValidBrowseSet) return false; if (!isOpen()) return false; bool ok = false; int fields = browseFields.count(); QString emptyvalue = curNewData; QString statement = "INSERT INTO "; statement.append(GetEncodedQString(curBrowseTableName)); statement.append(" VALUES("); for ( int i=1; i<=fields; i++ ) { statement.append(emptyvalue); if (i0) {//table exists getTableRecords( tablename ); browseFields = testFields; hasValidBrowseSet = true; curBrowseTableName = tablename; } else { hasValidBrowseSet = false; curBrowseTableName = QString(" "); browseFields.clear(); browseRecs.clear(); idmap.clear(); } return hasValidBrowseSet; } bool DBBrowserDB::createColumn( QString tablename, QString fieldname, QString fieldtype ){ qDebug("create column"); QString sql = QString("ALTER TABLE `%1` ADD COLUMN `%2` %3").arg(tablename).arg(fieldname).arg(fieldtype); qDebug(sql.toUtf8()); return executeSQL(sql); } bool DBBrowserDB::renameTable(QString from_table, QString to_table){ qDebug("renameTable column"); return true; } void DBBrowserDB::getTableRecords( const QString & tablename ) { sqlite3_stmt *vm; const char *tail; int ncol; QStringList r; // char *errmsg; int err=0; // int tabnum = 0; browseRecs.clear(); idmap.clear(); lastErrorMessage = QString("no error"); QString statement = "SELECT rowid, * FROM "; statement.append( GetEncodedQString(tablename) ); statement.append(" ORDER BY rowid; "); //qDebug(statement); logSQL(statement, kLogMsg_App); err=sqlite3_prepare(_db,statement.toUtf8(),statement.length(), &vm, &tail); if (err == SQLITE_OK){ int rownum = 0; while ( sqlite3_step(vm) == SQLITE_ROW ){ r.clear(); ncol = sqlite3_data_count(vm); for (int e=0; e loglimit)&&(msgtype==kLogMsg_App)) { statement.truncate(32); statement.append("... ..."); } logWin->log(statement, msgtype); } } void DBBrowserDB::updateSchema( ) { // qDebug ("Getting list of tables"); sqlite3_stmt *vm; const char *tail; QStringList r; int err=0; QString num; int idxnum =0; int tabnum = 0; idxmap.clear(); tbmap.clear(); lastErrorMessage = QString("no error"); QString statement = "SELECT name, sql " "FROM sqlite_master " "WHERE type='table' ;"; err=sqlite3_prepare(_db, (const char *) statement.toUtf8(),statement.length(), &vm, &tail); if (err == SQLITE_OK){ logSQL(statement, kLogMsg_App); while ( sqlite3_step(vm) == SQLITE_ROW ){ num.setNum(tabnum); QString val1, val2; val1 = QString((const char *) sqlite3_column_text(vm, 0)); val2 = QString((const char *) sqlite3_column_text(vm, 1)); tbmap[num] = DBBrowserTable(GetDecodedQString(val1), GetDecodedQString(val2)); tabnum++; } sqlite3_finalize(vm); }else{ qDebug ("could not get list of tables: %d, %s",err,sqlite3_errmsg(_db)); } //now get the field list for each table in tbmap tableMap::Iterator it; for ( it = tbmap.begin(); it != tbmap.end(); ++it ) { statement = "PRAGMA TABLE_INFO("; statement.append( it.value().getname().toUtf8().constData()); statement.append(");"); logSQL(statement, kLogMsg_App); err=sqlite3_prepare(_db,statement.toUtf8(),statement.length(), &vm, &tail); if (err == SQLITE_OK){ it.value(). fldmap.clear(); int e = 0; while ( sqlite3_step(vm) == SQLITE_ROW ){ if (sqlite3_column_count(vm)==6) { QString val1, val2; int ispk= 0; val1 = QString((const char *) sqlite3_column_text(vm, 1)); val2 = QString((const char *) sqlite3_column_text(vm, 2)); ispk = sqlite3_column_int(vm, 5); if (ispk==1){ val2.append(QString(" PRIMARY KEY")); } it.value().addField(e,GetDecodedQString(val1),GetDecodedQString(val2)); e++; } } sqlite3_finalize(vm); } else{ lastErrorMessage = QString ("could not get types"); } } statement = "SELECT name, sql " "FROM sqlite_master " "WHERE type='index' "; /*"ORDER BY name;"*/ //finally get indices err=sqlite3_prepare(_db,statement.toUtf8(),statement.length(), &vm, &tail); logSQL(statement, kLogMsg_App); if (err == SQLITE_OK){ while ( sqlite3_step(vm) == SQLITE_ROW ){ QString val1, val2; val1 = QString((const char *) sqlite3_column_text(vm, 0)); val2 = QString((const char *) sqlite3_column_text(vm, 1)); num.setNum(idxnum); idxmap[num] = DBBrowserIndex(GetDecodedQString(val1),GetDecodedQString(val2)); idxnum ++; } sqlite3_finalize(vm); }else{ lastErrorMessage = QString ("could not get list of indices"); } } QStringList DBBrowserDB::decodeCSV(const QString & csvfilename, char sep, char quote, int maxrecords, int * numfields) { QFile file(csvfilename); QStringList result; QString current = ""; bool inquotemode = false; bool inescapemode = false; int recs = 0; *numfields = 0; if ( file.open( QIODevice::ReadWrite ) ) { QProgressDialog progress("Decoding CSV file...", "Cancel", 0, file.size()); progress.setWindowModality(Qt::ApplicationModal); char c=0; while ( c!=-1) { file.getChar(&c); if (c==quote){ if (inquotemode){ if (inescapemode){ inescapemode = false; //add the escaped char here current.append(c); } else { //are we escaping, or just finishing the quote? char d; file.getChar(&d); if (d==quote) { inescapemode = true; } else { inquotemode = false; } file.ungetChar(d); } } else { inquotemode = true; } } else if (c==sep) { if (inquotemode){ //add the sep here current.append(c); } else { //not quoting, start new record result << current; current = ""; } } else if (c==10) { if (inquotemode){ //add the newline current.append(c); } else { //not quoting, start new record result << current; current = ""; //for the first line, store the field count if (*numfields == 0){ *numfields = result.count(); } recs++; progress.setValue(file.pos()); if (progress.wasCanceled()) break; if ((recs>maxrecords)&&(maxrecords!=-1)) { break; } } } else if (c==13) { if (inquotemode){ //add the carrier return if in quote mode only current.append(c); } } else {//another character type current.append(c); } } file.close(); //do we still have a last result, not appended? //proper csv files should end with a linefeed , so this is not necessary //if (current.length()>0) result << current; } return result; }