1 /****************************************************************************************
2  * Copyright (c) 2008 Edward Toroshchin <edward.hades@gmail.com>                        *
3  * Copyright (c) 2009 Jeff Mitchell <mitchell@kde.org>                                  *
4  *                                                                                      *
5  * This program is free software; you can redistribute it and/or modify it under        *
6  * the terms of the GNU General Public License as published by the Free Software        *
7  * Foundation; either version 2 of the License, or (at your option) any later           *
8  * version.                                                                             *
9  *                                                                                      *
10  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
11  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
12  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
13  *                                                                                      *
14  * You should have received a copy of the GNU General Public License along with         *
15  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
16  ****************************************************************************************/
17 
18 #define DEBUG_PREFIX "MySqlEmbeddedStorage"
19 
20 #include "MySqlEmbeddedStorage.h"
21 
22 #include <amarokconfig.h>
23 #include <core/support/Amarok.h>
24 #include <core/support/Debug.h>
25 
26 #include <QDir>
27 #include <QVarLengthArray>
28 #include <QVector>
29 #include <QAtomicInt>
30 
31 #include <mysql.h>
32 
33 /** number of times the library is used.
34  */
35 static QAtomicInt libraryInitRef;
36 
MySqlEmbeddedStorage()37 MySqlEmbeddedStorage::MySqlEmbeddedStorage()
38     : MySqlStorage()
39 {
40     m_debugIdent = "MySQLe";
41 }
42 
43 bool
init(const QString & storageLocation)44 MySqlEmbeddedStorage::init( const QString &storageLocation )
45 {
46     // -- figuring out and setting the database path.
47     QString storagePath = storageLocation;
48     QString databaseDir;
49     // TODO: the following logic is not explained in the comments.
50     //  tests use a different directory then the real run
51     if( storagePath.isEmpty() )
52     {
53         storagePath = Amarok::saveLocation();
54         databaseDir = Amarok::config( "MySQLe" ).readEntry( "data", QString(storagePath + "mysqle") );
55     }
56     else
57     {
58         QDir dir( storagePath );
59         dir.mkpath( "." );  //ensure directory exists
60         databaseDir = dir.absolutePath() + QDir::separator() + "mysqle";
61     }
62 
63     QVector<const char*> mysql_args;
64     QByteArray dataDir = QStringLiteral( "--datadir=%1" ).arg( databaseDir ).toLocal8Bit();
65     mysql_args << "amarok"
66                << dataDir.constData()
67                // CAUTION: if we ever change the table type we will need to fix a number of MYISAM specific
68                // functions, such as FULLTEXT indexing.
69                << "--default-storage-engine=MyISAM"
70                << "--innodb=OFF"
71                << "--skip-grant-tables"
72 #if (defined(MYSQL_VERSION_ID)) && (MYSQL_VERSION_ID >= 50700)
73                << "--myisam-recover-options=FORCE"
74 #else
75                << "--myisam-recover=FORCE"
76 #endif
77                << "--key-buffer-size=16777216" // (16Mb)
78                << "--character-set-server=utf8"
79                << "--collation-server=utf8_bin";
80 
81 
82     if( !QFile::exists( databaseDir ) )
83     {
84         QDir dir( databaseDir );
85         dir.mkpath( "." );
86     }
87 
88     // -- initializing the library
89     // we only need to do this once
90     if( !libraryInitRef.fetchAndAddOrdered( 1 ) )
91     {
92         int ret = mysql_library_init( mysql_args.size(), const_cast<char**>(mysql_args.data()), 0 );
93         if( ret != 0 )
94         {
95             // mysql sources show that there is only 0 and 1 as return code
96             // and it can only fail because of memory or thread issues.
97             reportError( "library initialization "
98                          "failed, return code " + QString::number( ret ) );
99             libraryInitRef.deref();
100             return false;
101         }
102     }
103 
104     m_db = mysql_init( NULL );
105     if( !m_db )
106     {
107         reportError( "call to mysql_init" );
108         return false;
109     }
110 
111     if( mysql_options( m_db, MYSQL_READ_DEFAULT_GROUP, "amarokclient" ) )
112         reportError( "Error setting options for READ_DEFAULT_GROUP" );
113     if( mysql_options( m_db, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL ) )
114         reportError( "Error setting option to use embedded connection" );
115 
116     if( !mysql_real_connect( m_db, NULL,NULL,NULL, 0, 0,NULL, 0 ) )
117     {
118         error() << "Could not connect to mysql embedded!";
119         reportError( "call to mysql_real_connect" );
120         mysql_close( m_db );
121         m_db = 0;
122         return false;
123     }
124 
125     if( !sharedInit( QLatin1String("amarok") ) )
126     {
127         // if sharedInit fails then we can usually not switch to the correct database
128         // sharedInit already reports errors.
129         mysql_close( m_db );
130         m_db = 0;
131         return false;
132     }
133 
134     MySqlStorage::initThreadInitializer();
135 
136     return true;
137 }
138 
~MySqlEmbeddedStorage()139 MySqlEmbeddedStorage::~MySqlEmbeddedStorage()
140 {
141     if( m_db )
142     {
143         mysql_close( m_db );
144         libraryInitRef.deref();
145     }
146 }
147 
148