diff --git a/src/core-impl/storage/sql/mysql-shared/MySqlStorage.cpp b/src/core-impl/storage/sql/mysql-shared/MySqlStorage.cpp index 6ec169105c..75b7ca6e16 100644 --- a/src/core-impl/storage/sql/mysql-shared/MySqlStorage.cpp +++ b/src/core-impl/storage/sql/mysql-shared/MySqlStorage.cpp @@ -1,314 +1,312 @@ /**************************************************************************************** * Copyright (c) 2008 Edward Toroshchin * * Copyright (c) 2009 Jeff Mitchell * * * * This program is free software; you can redistribute it and/or modify it under * * the terms of the GNU General Public License as published by the Free Software * * Foundation; either version 2 of the License, or (at your option) any later * * version. * * * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * * PARTICULAR PURPOSE. See the GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License along with * * this program. If not, see . * ****************************************************************************************/ #define DEBUG_PREFIX "MySqlStorage" #include "MySqlStorage.h" #include "core/support/Amarok.h" #include "core/support/Debug.h" #include "amarokconfig.h" #include #include #include #include /** * This class is used by MySqlStorage to fulfill mysql's thread * requirements. In every function that calls mysql_*, an init() method of * this class must be invoked. */ class ThreadInitializer { static int threadsCount; static QMutex countMutex; static QThreadStorage< ThreadInitializer* > storage; /** * This should be called ONLY by init() */ ThreadInitializer() { mysql_thread_init(); countMutex.lock(); threadsCount++; debug() << "Initialized thread, count ==" << threadsCount; countMutex.unlock(); } public: /** * This is called by QThreadStorage when a thread is destroyed */ ~ThreadInitializer() { mysql_thread_end(); countMutex.lock(); threadsCount--; debug() << "Deinitialized thread, count ==" << threadsCount; if( threadsCount == 0 ) mysql_library_end(); countMutex.unlock(); } static void init() { if( !storage.hasLocalData() ) storage.setLocalData( new ThreadInitializer() ); } }; int ThreadInitializer::threadsCount = 0; QMutex ThreadInitializer::countMutex; QThreadStorage< ThreadInitializer* > ThreadInitializer::storage; MySqlStorage::MySqlStorage() : SqlStorage() , m_db( 0 ) , m_mutex( QMutex::Recursive ) , m_debugIdent( "MySQL-none" ) { //Relevant code must be implemented in subclasses } MySqlStorage::~MySqlStorage() { } QStringList MySqlStorage::query( const QString& statement ) { //DEBUG_BLOCK //debug() << "[ATTN!] MySql::query( " << statement << " )"; initThreadInitializer(); QMutexLocker locker( &m_mutex ); QStringList values; if( !m_db ) { error() << "Tried to perform query on uninitialized MySQL"; return values; } int res = mysql_query( m_db, statement.toUtf8() ); if( res ) { reportError( statement ); return values; } MYSQL_RES *pres = mysql_store_result( m_db ); if( !pres ) // No results... check if any were expected { if( mysql_field_count( m_db ) ) reportError( statement ); return values; } int number = mysql_num_fields( pres ); if( number <= 0 ) { warning() << "Errr... query returned but with no fields"; } -#if QT_VERSION >= 0x040700 int rows = mysql_num_rows( pres ); values.reserve( rows ); -#endif MYSQL_ROW row = mysql_fetch_row( pres ); while( row ) { for( int i = 0; i < number; ++i ) { values << QString::fromUtf8( (const char*) row[i] ); } row = mysql_fetch_row( pres ); } mysql_free_result( pres ); return values; } int MySqlStorage::insert( const QString& statement, const QString& /* table */ ) { //DEBUG_BLOCK //debug() << "[ATTN!] MySql::insert( " << statement << " )"; initThreadInitializer(); QMutexLocker locker( &m_mutex ); if( !m_db ) { error() << "Tried to perform insert on uninitialized MySQL"; return 0; } int res = mysql_query( m_db, statement.toUtf8() ); if( res ) { reportError( statement ); return 0; } MYSQL_RES *pres = mysql_store_result( m_db ); if( pres ) { warning() << "[IMPORTANT!] insert returned data"; mysql_free_result( pres ); } res = mysql_insert_id( m_db ); return res; } QString MySqlStorage::escape( const QString &text ) const { if( !m_db ) { error() << "Tried to perform escape() on uninitialized MySQL"; return QString(); } const QByteArray utfText = text.toUtf8(); const int length = utfText.length() * 2 + 1; QVarLengthArray outputBuffer( length ); { QMutexLocker locker( &m_mutex ); mysql_real_escape_string( m_db, outputBuffer.data(), utfText.constData(), utfText.length() ); } return QString::fromUtf8( outputBuffer.constData() ); } QString MySqlStorage::randomFunc() const { return "RAND()"; } QString MySqlStorage::boolTrue() const { return "1"; } QString MySqlStorage::boolFalse() const { return "0"; } QString MySqlStorage::idType() const { return "INTEGER PRIMARY KEY AUTO_INCREMENT"; } QString MySqlStorage::textColumnType( int length ) const { return QString( "VARCHAR(%1)" ).arg( length ); } QString MySqlStorage::exactTextColumnType( int length ) const { return textColumnType( length ); } QString MySqlStorage::exactIndexableTextColumnType( int length ) const { return textColumnType( length ); } QString MySqlStorage::longTextColumnType() const { return "TEXT"; } QStringList MySqlStorage::getLastErrors() const { QMutexLocker locker( &m_mutex ); return m_lastErrors; } void MySqlStorage::clearLastErrors() { QMutexLocker locker( &m_mutex ); m_lastErrors.clear(); } void MySqlStorage::reportError( const QString& message ) { QMutexLocker locker( &m_mutex ); QString errorMessage; if( m_db ) errorMessage = m_debugIdent + " query failed! (" + QString::number( mysql_errno( m_db ) ) + ") " + mysql_error( m_db ) + " on " + message; else errorMessage = m_debugIdent + " something failed! on " + message; error() << errorMessage; if( m_lastErrors.count() < 20 ) m_lastErrors.append( errorMessage ); } void MySqlStorage::initThreadInitializer() { ThreadInitializer::init(); } bool MySqlStorage::sharedInit( const QString &databaseName ) { QMutexLocker locker( &m_mutex ); if( mysql_query( m_db, QString( "SET NAMES 'utf8'" ).toUtf8() ) ) reportError( "SET NAMES 'utf8' died" ); if( mysql_query( m_db, QString( "CREATE DATABASE IF NOT EXISTS %1 DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_bin" ).arg( databaseName ).toUtf8() ) ) reportError( QString( "Could not create %1 database" ).arg( databaseName ) ); if( mysql_query( m_db, QString( "ALTER DATABASE %1 DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_bin" ).arg( databaseName ).toUtf8() ) ) reportError( "Could not alter database charset/collation" ); if( mysql_query( m_db, QString( "USE %1" ).arg( databaseName ).toUtf8() ) ) { reportError( "Could not select database" ); return false; // this error is fatal } debug() << "Connected to MySQL server" << mysql_get_server_info( m_db ); return true; } diff --git a/src/core/support/Debug.cpp b/src/core/support/Debug.cpp index 3d7701006f..0e2a83a884 100644 --- a/src/core/support/Debug.cpp +++ b/src/core/support/Debug.cpp @@ -1,233 +1,225 @@ /* Copyright (c) 2003-2005 Max Howell Copyright (c) 2007-2009 Mark Kretschmann Copyright (c) 2010-2011 Kevin Funk This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "amarok_export.h" #include "core/support/Debug.h" #include "core/support/Debug_p.h" #include #include #include #include #include #include // Define Application wide prefix #ifndef APP_PREFIX #define APP_PREFIX QLatin1String( "amarok:" ) #endif #define DEBUG_INDENT_OBJECTNAME QLatin1String("Debug_Indent_object") AMAROK_CORE_EXPORT QMutex Debug::mutex( QMutex::Recursive ); using namespace Debug; static bool s_debugEnabled = false; static bool s_debugColorsEnabled = false; Q_GLOBAL_STATIC( NoDebugStream, s_noDebugStream ) IndentPrivate::IndentPrivate(QObject* parent) : QObject(parent) { setObjectName( DEBUG_INDENT_OBJECTNAME ); } /** * We can't use a statically instantiated QString for the indent, because * static namespaces are unique to each dlopened library. So we piggy back * the QString on the QApplication instance */ IndentPrivate* IndentPrivate::instance() { QObject* qOApp = reinterpret_cast(qApp); QObject* obj = qOApp ? qOApp->findChild( DEBUG_INDENT_OBJECTNAME ) : 0; return (obj ? static_cast( obj ) : new IndentPrivate( qApp )); } /* Text color codes (use last digit here) 30=black 31=red 32=green 33=yellow 34=blue 35=magenta 36=cyan 37=white */ static int s_colors[] = { 1, 2, 4, 5, 6 }; // no yellow and white for sanity static int s_colorIndex = 0; static QString toString( DebugLevel level ) { switch( level ) { case KDEBUG_WARN: return "[WARNING]"; case KDEBUG_ERROR: return "[ERROR__]"; case KDEBUG_FATAL: return "[FATAL__]"; default: return QString(); } } static int toColor( DebugLevel level ) { switch( level ) { case KDEBUG_WARN: return 3; // red case KDEBUG_ERROR: case KDEBUG_FATAL: return 1; // yellow default: return 0; // default: black } } static QString colorize( const QString &text, int color = s_colorIndex ) { if( !debugColorEnabled() ) return text; return QString( "\x1b[00;3%1m%2\x1b[00;39m" ).arg( QString::number(s_colors[color]), text ); } static QString reverseColorize( const QString &text, int color ) { if( !debugColorEnabled() ) return text; return QString( "\x1b[07;3%1m%2\x1b[00;39m" ).arg( QString::number(color), text ); } QString Debug::indent() { return IndentPrivate::instance()->m_string; } bool Debug::debugEnabled() { return s_debugEnabled; } bool Debug::debugColorEnabled() { return s_debugColorsEnabled; } void Debug::setDebugEnabled( bool enable ) { s_debugEnabled = enable; } void Debug::setColoredDebug( bool enable ) { s_debugColorsEnabled = enable; } QDebug Debug::dbgstream( DebugLevel level ) { if( !debugEnabled() ) return QDebug( s_noDebugStream ); mutex.lock(); const QString currentIndent = indent(); mutex.unlock(); QString text = QString("%1%2").arg( APP_PREFIX ).arg( currentIndent ); if ( level > KDEBUG_INFO ) text.append( ' ' + reverseColorize( toString(level), toColor( level ) ) ); return QDebug( QtDebugMsg ) << qPrintable( text ); } void Debug::perfLog( const QString &message, const QString &func ) { #ifdef Q_OS_UNIX if( !debugEnabled() ) return; QString str = QString( "MARK: %1: %2 %3" ).arg( qApp->applicationName(), func, message ); access( str.toLocal8Bit().data(), F_OK ); #endif } Block::Block( const char *label ) : m_label( label ) , m_color( s_colorIndex ) { if( !debugEnabled() ) return; -#if QT_VERSION >= 0x040700 m_startTime.start(); -#else - m_startTime = QTime::currentTime(); -#endif mutex.lock(); s_colorIndex = (s_colorIndex + 1) % 5; dbgstream() << qPrintable( colorize( QLatin1String( "BEGIN:" ), m_color ) ) << m_label; IndentPrivate::instance()->m_string += QLatin1String(" "); mutex.unlock(); } Block::~Block() { if( !debugEnabled() ) return; -#if QT_VERSION >= 0x040700 double duration = m_startTime.elapsed() / 1000.0; -#else - double duration = (double)m_startTime.msecsTo( QTime::currentTime() ) / 1000.0; -#endif mutex.lock(); IndentPrivate::instance()->m_string.truncate( Debug::indent().length() - 2 ); mutex.unlock(); #ifdef DEBUG_OVERRIDE_ELAPSED_TIME duration = DEBUG_OVERRIDE_ELAPSED_TIME; #endif // Print timing information, and a special message (DELAY) if the method took longer than 5s if( duration < 5.0 ) { dbgstream() << qPrintable( colorize( QLatin1String( "END__:" ), m_color ) ) << m_label << qPrintable( colorize( QString( "[Took: %3s]") .arg( QString::number(duration, 'g', 2) ), m_color ) ); } else { dbgstream() << qPrintable( colorize( QString( "END__:" ), m_color ) ) << m_label << qPrintable( reverseColorize( QString( "[DELAY Took (quite long) %3s]") .arg( QString::number(duration, 'g', 2) ), toColor( KDEBUG_WARN ) ) ); } } void Debug::stamp() { static int n = 0; debug() << "| Stamp: " << ++n << endl; } diff --git a/src/core/support/Debug.h b/src/core/support/Debug.h index 883ab49347..21909e9204 100644 --- a/src/core/support/Debug.h +++ b/src/core/support/Debug.h @@ -1,259 +1,255 @@ /* Copyright (c) 2003-2005 Max Howell Copyright (c) 2007-2009 Mark Kretschmann Copyright (c) 2010-2011 Kevin Funk This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef AMAROK_DEBUG_H #define AMAROK_DEBUG_H // We always want debug output available at runtime #undef QT_NO_DEBUG_OUTPUT #undef KDE_NO_DEBUG_OUTPUT #include "core/amarokcore_export.h" #include #include #include // BEGIN: DEBUG_ASSERT /** * Debug helper to write "soft" assertions with escape statements more easily * If the assertions fails, a warning is printed containing the position * (file and line number) of the assert and the second parameter is evaluated. * * Usage: DEBUG_ASSERT(assertion, statement) * * (pseudo code *without* DEBUG_ASSERT) * \code * bool someMethod(T* pointer) { * if (!pointer) * qWarning() << "Warning pointer is null, aborting"; * return false; * (...) * return someBoolean; * } * \endcode * * (may be replaced by) * \code * bool someMethod(T* pointer) { * DEBUG_ASSERT(pointer, return false) * (...) * return someBoolean; * } * \endcode * * \author Kevin Funk * \sa http://qt.gitorious.org/qt-creator/qt-creator/blobs/master/src/libs/utils/qtcassert.h */ #define DEBUG_ASSERT(cond, action) \ if(cond){}else{warning()<< \ "ASSERTION " #cond " FAILED AT " __FILE__ ":" DEBUG_ASSERT_STRINGIFY(__LINE__);action;} #define DEBUG_ASSERT_STRINGIFY_INTERNAL(x) #x #define DEBUG_ASSERT_STRINGIFY(x) DEBUG_ASSERT_STRINGIFY_INTERNAL(x) // END__: DEBUG_ASSERT -#if QT_VERSION >= 0x040700 # include -#else -# include -#endif // Platform specific macros #ifdef _WIN32 #define __PRETTY_FUNCTION__ __FUNCTION__ #endif #ifdef __SUNPRO_CC #define __PRETTY_FUNCTION__ __FILE__ #endif // Debug prefix, override if needed #ifndef DEBUG_PREFIX #define AMAROK_PREFIX "" #else #define AMAROK_PREFIX "[" DEBUG_PREFIX "]" #endif /** * @namespace Debug * @short kdebug with indentation functionality and convenience macros * @author Max Howell * * Usage: * * #define DEBUG_PREFIX "Blah" * #include "debug.h" * * void function() * { * Debug::Block myBlock( __PRETTY_FUNCTION__ ); * * debug() << "output1" << endl; * debug() << "output2" << endl; * } * * Will output: * * app: BEGIN: void function() * app: [Blah] output1 * app: [Blah] output2 * app: END: void function(): Took 0.1s * * @see Block * @see CrashHelper * @see ListStream */ namespace Debug { extern AMAROK_CORE_EXPORT QMutex mutex; // from kdebug.h enum DebugLevel { KDEBUG_INFO = 0, KDEBUG_WARN = 1, KDEBUG_ERROR = 2, KDEBUG_FATAL = 3 }; AMAROK_CORE_EXPORT QDebug dbgstream( DebugLevel level = KDEBUG_INFO ); AMAROK_CORE_EXPORT bool debugEnabled(); AMAROK_CORE_EXPORT bool debugColorEnabled(); AMAROK_CORE_EXPORT void setDebugEnabled( bool enable ); AMAROK_CORE_EXPORT void setColoredDebug( bool enable ); AMAROK_CORE_EXPORT QString indent(); static inline QDebug dbgstreamwrapper( DebugLevel level ) { #ifdef DEBUG_PREFIX return dbgstream( level ) << AMAROK_PREFIX; #else return dbgstream( level ); #endif } static inline QDebug debug() { return dbgstreamwrapper( KDEBUG_INFO ); } static inline QDebug warning() { return dbgstreamwrapper( KDEBUG_WARN ); } static inline QDebug error() { return dbgstreamwrapper( KDEBUG_ERROR ); } static inline QDebug fatal() { return dbgstreamwrapper( KDEBUG_FATAL ); } AMAROK_CORE_EXPORT void perfLog( const QString &message, const QString &func ); } using Debug::debug; using Debug::warning; using Debug::error; using Debug::fatal; /// Standard function announcer #define DEBUG_FUNC_INFO { Debug::mutex.lock(); qDebug() << Debug::indent() ; Debug::mutex.unlock(); } /// Announce a line #define DEBUG_LINE_INFO { Debug::mutex.lock(); qDebug() << Debug::indent() << "Line: " << __LINE__; Debug::mutex.unlock(); } /// Convenience macro for making a standard Debug::Block #define DEBUG_BLOCK Debug::Block uniquelyNamedStackAllocatedStandardBlock( __PRETTY_FUNCTION__ ); /// Use this to remind yourself to finish the implementation of a function #define AMAROK_NOTIMPLEMENTED warning() << "NOT-IMPLEMENTED:" << __PRETTY_FUNCTION__ << endl; /// Use this to alert other developers to stop using a function #define AMAROK_DEPRECATED warning() << "DEPRECATED:" << __PRETTY_FUNCTION__ << endl; /// Performance logging #define PERF_LOG( msg ) { Debug::perfLog( msg, __PRETTY_FUNCTION__ ); } class BlockPrivate; namespace Debug { /** * @class Debug::Block * @short Use this to label sections of your code * * Usage: * * void function() * { * Debug::Block myBlock( "section" ); * * debug() << "output1" << endl; * debug() << "output2" << endl; * } * * Will output: * * app: BEGIN: section * app: [prefix] output1 * app: [prefix] output2 * app: END: section - Took 0.1s * */ class Block { public: AMAROK_CORE_EXPORT Block( const char *name ); AMAROK_CORE_EXPORT ~Block(); private: QElapsedTimer m_startTime; const char *m_label; int m_color; }; /** * @name Debug::stamp() * @short To facilitate crash/freeze bugs, by making it easy to mark code that has been processed * * Usage: * * { * Debug::stamp(); * function1(); * Debug::stamp(); * function2(); * Debug::stamp(); * } * * Will output (assuming the crash occurs in function2() * * app: Stamp: 1 * app: Stamp: 2 * */ AMAROK_CORE_EXPORT void stamp(); /** * @class Debug::List * @short You can pass anything to this and it will output it as a list * * debug() << (Debug::List() << anInt << aString << aQStringList << aDouble) << endl; */ typedef QList List; } template class AmarokSharedPointer; template QDebug & operator<<( QDebug dbg, const AmarokSharedPointer &ptr ) { dbg.nospace() << "AmarokSharedPointer(" << *ptr.data() << ")"; return dbg.space(); } #endif