kexi

sqliteconnection.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2003-2006 Jaroslaw Staniek <js@iidea.pl>
00003 
00004    This program is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this program; see the file COPYING.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "sqliteconnection.h"
00021 #include "sqliteconnection_p.h"
00022 #include "sqlitecursor.h"
00023 #include "sqlitepreparedstatement.h"
00024 
00025 #include "sqlite.h"
00026 
00027 #ifndef SQLITE2
00028 # include "kexisql.h" //for isReadOnly()
00029 #endif
00030 
00031 #include <kexidb/driver.h>
00032 #include <kexidb/cursor.h>
00033 #include <kexidb/error.h>
00034 #include <kexiutils/utils.h>
00035 
00036 #include <qfile.h>
00037 #include <qdir.h>
00038 #include <qregexp.h>
00039 
00040 #include <kgenericfactory.h>
00041 #include <kdebug.h>
00042 
00043 //remove debug
00044 #undef KexiDBDrvDbg
00045 #define KexiDBDrvDbg if (0) kdDebug()
00046 
00047 using namespace KexiDB;
00048 
00049 SQLiteConnectionInternal::SQLiteConnectionInternal(Connection *connection)
00050  : ConnectionInternal(connection)
00051  , data(0)
00052  , data_owned(true)
00053  , errmsg_p(0)
00054  , res(SQLITE_OK)
00055  , temp_st(0x10000)
00056 #ifdef SQLITE3
00057  , result_name(0)
00058 #endif
00059 {
00060 }
00061 
00062 SQLiteConnectionInternal::~SQLiteConnectionInternal() 
00063 {
00064     if (data_owned && data) {
00065         free( data ); 
00066         data = 0;
00067     }
00068 //sqlite_freemem does this  if (errmsg) {
00069 //      free( errmsg );
00070 //      errmsg = 0;
00071 //  }
00072 }
00073 
00074 void SQLiteConnectionInternal::storeResult()
00075 {
00076     if (errmsg_p) {
00077         errmsg = errmsg_p;
00078         sqlite_free(errmsg_p);
00079         errmsg_p = 0;
00080     }
00081 #ifdef SQLITE3
00082     errmsg = (data && res!=SQLITE_OK) ? sqlite3_errmsg(data) : 0;
00083 #endif
00084 }
00085 
00087 SQLiteConnection::SQLiteConnection( Driver *driver, ConnectionData &conn_data )
00088     : Connection( driver, conn_data )
00089     ,d(new SQLiteConnectionInternal(this))
00090 {
00091 }
00092 
00093 SQLiteConnection::~SQLiteConnection()
00094 {
00095     KexiDBDrvDbg << "SQLiteConnection::~SQLiteConnection()" << endl;
00096     //disconnect if was connected
00097 //  disconnect();
00098     destroy();
00099     delete d;
00100     KexiDBDrvDbg << "SQLiteConnection::~SQLiteConnection() ok" << endl;
00101 }
00102 
00103 bool SQLiteConnection::drv_connect(KexiDB::ServerVersionInfo& version)
00104 {
00105     KexiDBDrvDbg << "SQLiteConnection::connect()" << endl;
00106     version.string = QString(SQLITE_VERSION); //defined in sqlite3.h
00107     QRegExp re("(\\d+)\\.(\\d+)\\.(\\d+)");
00108     if (re.exactMatch(version.string)) {
00109         version.major = re.cap(1).toUInt();
00110         version.minor = re.cap(2).toUInt();
00111         version.release = re.cap(3).toUInt();
00112     }
00113     return true;
00114 }
00115 
00116 bool SQLiteConnection::drv_disconnect()
00117 {
00118     KexiDBDrvDbg << "SQLiteConnection::disconnect()" << endl;
00119     return true;
00120 }
00121 
00122 bool SQLiteConnection::drv_getDatabasesList( QStringList &list )
00123 {
00124     //this is one-db-per-file database
00125     list.append( data()->fileName() ); //more consistent than dbFileName() ?
00126     return true;
00127 }
00128 
00129 bool SQLiteConnection::drv_containsTable( const QString &tableName )
00130 {
00131     bool success;
00132     return resultExists(QString("select name from sqlite_master where type='table' and name LIKE %1")
00133         .arg(driver()->escapeString(tableName)), success) && success;
00134 }
00135 
00136 bool SQLiteConnection::drv_getTablesList( QStringList &list )
00137 {
00138     KexiDB::Cursor *cursor;
00139     m_sql = "select lower(name) from sqlite_master where type='table'";
00140     if (!(cursor = executeQuery( m_sql ))) {
00141         KexiDBWarn << "Connection::drv_getTablesList(): !executeQuery()" << endl;
00142         return false;
00143     }
00144     list.clear();
00145     cursor->moveFirst();
00146     while (!cursor->eof() && !cursor->error()) {
00147         list += cursor->value(0).toString();
00148         cursor->moveNext();
00149     }
00150     if (cursor->error()) {
00151         deleteCursor(cursor);
00152         return false;
00153     }
00154     return deleteCursor(cursor);
00155 }
00156 
00157 bool SQLiteConnection::drv_createDatabase( const QString &dbName )
00158 {
00159     // SQLite creates a new db is it does not exist
00160     return drv_useDatabase(dbName);
00161 #if 0
00162     d->data = sqlite_open( QFile::encodeName( data()->fileName() ), 0/*mode: unused*/, 
00163         &d->errmsg_p );
00164     d->storeResult();
00165     return d->data != 0;
00166 #endif
00167 }
00168 
00169 bool SQLiteConnection::drv_useDatabase( const QString &dbName, bool *cancelled, 
00170     MessageHandler* msgHandler )
00171 {
00172     Q_UNUSED(dbName);
00173 //  KexiDBDrvDbg << "drv_useDatabase(): " << data()->fileName() << endl;
00174 #ifdef SQLITE2
00175     Q_UNUSED(cancelled);
00176     Q_UNUSED(msgHandler);
00177     d->data = sqlite_open( QFile::encodeName( data()->fileName() ), 0/*mode: unused*/, 
00178         &d->errmsg_p );
00179     d->storeResult();
00180     return d->data != 0;
00181 #else //SQLITE3
00182     //TODO: perhaps allow to use sqlite3_open16() as well for SQLite ~ 3.3 ?
00184     int exclusiveFlag = Connection::isReadOnly() ? SQLITE_OPEN_READONLY : SQLITE_OPEN_WRITE_LOCKED; // <-- shared read + (if !r/o): exclusive write
00186     int allowReadonly = 1;
00187     const bool wasReadOnly = Connection::isReadOnly();
00188 
00189     d->res = sqlite3_open( 
00190         //QFile::encodeName( data()->fileName() ), 
00191         data()->fileName().utf8(), /* unicode expected since SQLite 3.1 */
00192         &d->data,
00193         exclusiveFlag,
00194         allowReadonly /* If 1 and locking fails, try opening in read-only mode */
00195     );
00196     d->storeResult();
00197 
00198     if (d->res == SQLITE_OK && cancelled && !wasReadOnly && allowReadonly && isReadOnly()) {
00199         //opened as read only, ask
00200         if (KMessageBox::Continue != 
00201             askQuestion( 
00202             i18n("Do you want to open file \"%1\" as read-only?")
00203                 .arg(QDir::convertSeparators(data()->fileName()))
00204             + "\n\n"
00205             + i18n("The file is probably already open on this or another computer.") + " "
00206             + i18n("Could not gain exclusive access for writing the file."),
00207             KMessageBox::WarningContinueCancel, KMessageBox::Continue, 
00208             KGuiItem(i18n("Open As Read-Only"), "fileopen"), KStdGuiItem::cancel(),
00209             "askBeforeOpeningFileReadOnly", KMessageBox::Notify, msgHandler ))
00210         {
00211             clearError();
00212             if (!drv_closeDatabase())
00213                 return false;
00214             *cancelled = true;
00215             return false;
00216         }
00217     }
00218 
00219     if (d->res == SQLITE_CANTOPEN_WITH_LOCKED_READWRITE) {
00220         setError(ERR_ACCESS_RIGHTS, 
00221         i18n("The file is probably already open on this or another computer.")+"\n\n"
00222         + i18n("Could not gain exclusive access for reading and writing the file.") + " "
00223         + i18n("Check the file's permissions and whether it is already opened and locked by another application."));
00224     }
00225     else if (d->res == SQLITE_CANTOPEN_WITH_LOCKED_WRITE) {
00226         setError(ERR_ACCESS_RIGHTS, 
00227         i18n("The file is probably already open on this or another computer.")+"\n\n"
00228         + i18n("Could not gain exclusive access for writing the file.") + " "
00229         + i18n("Check the file's permissions and whether it is already opened and locked by another application."));
00230     }
00231     return d->res == SQLITE_OK;
00232 #endif
00233 }
00234 
00235 bool SQLiteConnection::drv_closeDatabase()
00236 {
00237     if (!d->data)
00238         return false;
00239 
00240 #ifdef SQLITE2
00241     sqlite_close(d->data);
00242     d->data = 0;
00243     return true;
00244 #else
00245     const int res = sqlite_close(d->data);
00246     if (SQLITE_OK == res) {
00247         d->data = 0;
00248         return true;
00249     }
00250     if (SQLITE_BUSY==res) {
00251 #if 0 //this is ANNOYING, needs fixing (by closing cursors or waiting)
00252         setError(ERR_CLOSE_FAILED, i18n("Could not close busy database."));
00253 #else
00254         return true;
00255 #endif
00256     }
00257     return false;
00258 #endif
00259 }
00260 
00261 bool SQLiteConnection::drv_dropDatabase( const QString &dbName )
00262 {
00263     Q_UNUSED(dbName); // Each database is one single SQLite file.
00264     const QString filename = data()->fileName();
00265     if (QFile(filename).exists() && !QDir().remove(filename)) {
00266         setError(ERR_ACCESS_RIGHTS, i18n("Could not remove file \"%1\".")
00267             .arg(QDir::convertSeparators(filename)) + " "
00268             + i18n("Check the file's permissions and whether it is already opened and locked by another application."));
00269         return false;
00270     }
00271     return true;
00272 }
00273 
00274 //CursorData* SQLiteConnection::drv_createCursor( const QString& statement )
00275 Cursor* SQLiteConnection::prepareQuery( const QString& statement, uint cursor_options )
00276 {
00277     return new SQLiteCursor( this, statement, cursor_options );
00278 }
00279 
00280 Cursor* SQLiteConnection::prepareQuery( QuerySchema& query, uint cursor_options )
00281 {
00282     return new SQLiteCursor( this, query, cursor_options );
00283 }
00284 
00285 bool SQLiteConnection::drv_executeSQL( const QString& statement )
00286 {
00287 //  KexiDBDrvDbg << "SQLiteConnection::drv_executeSQL(" << statement << ")" <<endl;
00288 //  QCString st(statement.length()*2);
00289 //  st = escapeString( statement.local8Bit() ); //?
00290 #ifdef SQLITE_UTF8
00291     d->temp_st = statement.utf8();
00292 #else
00293     d->temp_st = statement.local8Bit(); //latin1 only
00294 #endif
00295 
00296 #ifdef KEXI_DEBUG_GUI
00297     KexiUtils::addKexiDBDebug(QString("ExecuteSQL (SQLite): ")+statement);
00298 #endif
00299 
00300     d->res = sqlite_exec( 
00301         d->data, 
00302         (const char*)d->temp_st, 
00303         0/*callback*/, 
00304         0,
00305         &d->errmsg_p );
00306     d->storeResult();
00307 #ifdef KEXI_DEBUG_GUI
00308     KexiUtils::addKexiDBDebug(d->res==SQLITE_OK ? "  Success" : "  Failure");
00309 #endif
00310     return d->res==SQLITE_OK;
00311 }
00312 
00313 Q_ULLONG SQLiteConnection::drv_lastInsertRowID()
00314 {
00315     return (Q_ULLONG)sqlite_last_insert_rowid(d->data);
00316 }
00317 
00318 int SQLiteConnection::serverResult()
00319 {
00320     return d->res==0 ? Connection::serverResult() : d->res;
00321 }
00322 
00323 QString SQLiteConnection::serverResultName()
00324 {
00325     QString r = 
00326 #ifdef SQLITE2
00327         QString::fromLatin1( sqlite_error_string(d->res) );
00328 #else //SQLITE3
00329         QString::null; //fromLatin1( d->result_name );
00330 #endif
00331     return r.isEmpty() ? Connection::serverResultName() : r;
00332 }
00333 
00334 void SQLiteConnection::drv_clearServerResult()
00335 {
00336     if (!d)
00337         return;
00338     d->res = SQLITE_OK;
00339 #ifdef SQLITE2
00340     d->errmsg_p = 0;
00341 #else
00342 //  d->result_name = 0;
00343 #endif
00344 }
00345 
00346 QString SQLiteConnection::serverErrorMsg()
00347 {
00348     return d->errmsg.isEmpty() ? Connection::serverErrorMsg() : d->errmsg;
00349 }
00350 
00351 PreparedStatement::Ptr SQLiteConnection::prepareStatement(PreparedStatement::StatementType type, 
00352     FieldList& fields)
00353 {
00354 //#ifndef SQLITE2 //TEMP IFDEF!
00355     return new SQLitePreparedStatement(type, *d, fields);
00356 //#endif
00357 }
00358 
00359 bool SQLiteConnection::isReadOnly() const
00360 {
00361 #ifdef SQLITE2
00362     return Connection::isReadOnly();
00363 #else
00364     return d->data ? sqlite3_is_readonly(d->data) : false;
00365 #endif
00366 }
00367 
00368 #ifdef SQLITE2
00369 bool SQLiteConnection::drv_alterTableName(TableSchema& tableSchema, const QString& newName, bool replace)
00370 {
00371     const QString oldTableName = tableSchema.name();
00372     const bool destTableExists = this->tableSchema( newName ) != 0;
00373 
00374     //1. drop the table
00375     if (destTableExists) {
00376         if (!replace)
00377             return false;
00378         if (!drv_dropTable( newName ))
00379             return false;
00380     }
00381 
00382     //2. create a copy of the table
00383 //TODO: move this code to drv_copyTable()
00384     tableSchema.setName(newName);
00385 
00386 //helper:
00387 #define drv_alterTableName_ERR \
00388         tableSchema.setName(oldTableName) //restore old name
00389 
00390     if (!drv_createTable( tableSchema )) {
00391         drv_alterTableName_ERR;
00392         return false;
00393     }
00394 
00395 //TODO indices, etc.???
00396 
00397     // 3. copy all rows to the new table
00398     if (!executeSQL(QString::fromLatin1("INSERT INTO %1 SELECT * FROM %2")
00399         .arg(escapeIdentifier(tableSchema.name())).arg(escapeIdentifier(oldTableName))))
00400     {
00401         drv_alterTableName_ERR;
00402         return false;
00403     }
00404 
00405     // 4. drop old table.
00406     if (!drv_dropTable( oldTableName )) {
00407         drv_alterTableName_ERR;
00408         return false;
00409     }
00410     return true;
00411 }
00412 #endif
00413 
00414 #include "sqliteconnection.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys