kexi

pqxxconnection.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2003 Adam Pigg <adam@piggz.co.uk>
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 "pqxxconnection.h"
00021 #include <qvariant.h>
00022 #include <qfile.h>
00023 #include <kdebug.h>
00024 #include <kexidb/error.h>
00025 #include <kexidb/global.h>
00026 #include <klocale.h>
00027 #include <string>
00028 #include "pqxxpreparedstatement.h"
00029 #include "pqxxconnection_p.h"
00030 using namespace KexiDB;
00031 
00032 pqxxTransactionData::pqxxTransactionData(Connection *conn, bool nontransaction)
00033  : TransactionData(conn)
00034 {
00035     if (nontransaction)
00036         data = new pqxx::nontransaction(*static_cast<pqxxSqlConnection*>(conn)->d->pqxxsql /* todo: add name? */);
00037     else
00038         data = new pqxx::transaction<>(*static_cast<pqxxSqlConnection*>(conn)->d->pqxxsql /* todo: add name? */);
00039     if (!static_cast<pqxxSqlConnection*>(conn)->m_trans) {
00040         static_cast<pqxxSqlConnection*>(conn)->m_trans = this;
00041     }
00042 }
00043 
00044 pqxxTransactionData::~pqxxTransactionData()
00045 {
00046     if (static_cast<pqxxSqlConnection*>(m_conn)->m_trans == this) {
00047         static_cast<pqxxSqlConnection*>(m_conn)->m_trans = 0;
00048     }
00049     delete data;
00050     data = 0;
00051 }
00052 
00053 //==================================================================================
00054 
00055 pqxxSqlConnection::pqxxSqlConnection(Driver *driver, ConnectionData &conn_data)
00056  : Connection(driver,conn_data)
00057  , d( new pqxxSqlConnectionInternal(this) )
00058  , m_trans(0)
00059 {
00060 }
00061 
00062 //==================================================================================
00063 //Do any tidying up before the object is deleted
00064 pqxxSqlConnection::~pqxxSqlConnection()
00065 {
00066     //delete m_trans;
00067     destroy();
00068     delete d;
00069 }
00070 
00071 //==================================================================================
00072 //Return a new query based on a query statment
00073 Cursor* pqxxSqlConnection::prepareQuery( const QString& statement,  uint cursor_options)
00074 {
00075     Q_UNUSED(cursor_options);
00076     return new pqxxSqlCursor(this, statement, 1); //Always used buffered cursor
00077 }
00078 
00079 //==================================================================================
00080 //Return a new query based on a query object
00081 Cursor* pqxxSqlConnection::prepareQuery( QuerySchema& query, uint cursor_options)
00082 {
00083     Q_UNUSED(cursor_options);
00084     return new pqxxSqlCursor(this, query, 1);//Always used buffered cursor
00085 }
00086 
00087 //==================================================================================
00088 //Properly escaped a database object name
00089 QString pqxxSqlConnection::escapeName(const QString &name) const
00090 {
00091     return QString("\"" + name + "\"");
00092 }
00093 
00094 //==================================================================================
00095 //Made this a noop
00096 //We tell kexi we are connected, but we wont actually connect until we use a database!
00097 bool pqxxSqlConnection::drv_connect(KexiDB::ServerVersionInfo& version)
00098 {
00099     KexiDBDrvDbg << "pqxxSqlConnection::drv_connect" << endl;
00100     version.clear();
00101     d->version = &version; //remember for later...
00102 #ifdef __GNUC__
00103 #warning pqxxSqlConnection::drv_connect implement setting version info when we drop libpqxx for libpq
00104 #endif
00105     return true;
00106 }
00107 
00108 //==================================================================================
00109 //Made this a noop
00110 //We tell kexi wehave disconnected, but it is actually handled by closeDatabse
00111 bool pqxxSqlConnection::drv_disconnect()
00112 {
00113     KexiDBDrvDbg << "pqxxSqlConnection::drv_disconnect: " << endl;
00114     return true;
00115 }
00116 
00117 //==================================================================================
00118 //Return a list of database names
00119 bool pqxxSqlConnection::drv_getDatabasesList( QStringList &list )
00120 {
00121 //  KexiDBDrvDbg << "pqxxSqlConnection::drv_getDatabaseList" << endl;
00122 
00123     if (executeSQL("SELECT datname FROM pg_database WHERE datallowconn = TRUE"))
00124     {
00125         std::string N;
00126         for (pqxx::result::const_iterator c = d->res->begin(); c != d->res->end(); ++c)
00127         {
00128             // Read value of column 0 into a string N
00129             c[0].to(N);
00130             // Copy the result into the return list
00131             list << QString::fromLatin1 (N.c_str());
00132         }
00133         return true;
00134     }
00135 
00136     return false;
00137 }
00138 
00139 //==================================================================================
00140 //Create a new database
00141 bool pqxxSqlConnection::drv_createDatabase( const QString &dbName )
00142 {
00143     KexiDBDrvDbg << "pqxxSqlConnection::drv_createDatabase: " << dbName << endl;
00144 
00145     if (executeSQL("CREATE DATABASE " + escapeName(dbName)))
00146         return true;
00147 
00148     return false;
00149 }
00150 
00151 //==================================================================================
00152 //Use this as our connection instead of connect
00153 bool pqxxSqlConnection::drv_useDatabase( const QString &dbName, bool *cancelled, 
00154     MessageHandler* msgHandler )
00155 {
00156     Q_UNUSED(cancelled);
00157     Q_UNUSED(msgHandler);
00158     KexiDBDrvDbg << "pqxxSqlConnection::drv_useDatabase: " << dbName << endl;
00159 
00160     QString conninfo;
00161     QString socket;
00162     QStringList sockets;
00163 
00164     if (data()->hostName.isEmpty() || data()->hostName == "localhost")
00165     {
00166         if (data()->localSocketFileName.isEmpty())
00167         {
00168             sockets.append("/tmp/.s.PGSQL.5432");
00169 
00170             for(QStringList::ConstIterator it = sockets.constBegin(); it != sockets.constEnd(); it++)
00171             {
00172                 if(QFile(*it).exists())
00173                 {
00174                     socket = (*it);
00175                     break;
00176                 }
00177             }
00178         }
00179         else
00180         {
00181             socket=data()->localSocketFileName; //data()->fileName();
00182         }
00183     }
00184     else
00185     {
00186         conninfo = "host='" + data()->hostName + "'";
00187     }
00188 
00189     //Build up the connection string
00190     if (data()->port == 0)
00191         data()->port = 5432;
00192 
00193     conninfo += QString::fromLatin1(" port='%1'").arg(data()->port);
00194 
00195     conninfo += QString::fromLatin1(" dbname='%1'").arg(dbName);
00196 
00197     if (!data()->userName.isNull())
00198         conninfo += QString::fromLatin1(" user='%1'").arg(data()->userName);
00199 
00200     if (!data()->password.isNull())
00201         conninfo += QString::fromLatin1(" password='%1'").arg(data()->password);
00202 
00203     try
00204     {
00205         d->pqxxsql = new pqxx::connection( conninfo.latin1() );
00206         drv_executeSQL( "SET DEFAULT_WITH_OIDS TO ON" ); //Postgres 8.1 changed the default to no oids but we need them
00207 
00208         if (d->version) {
00210         }
00211         return true;
00212     }
00213     catch(const std::exception &e)
00214     {
00215         KexiDBDrvDbg << "pqxxSqlConnection::drv_useDatabase:exception - " << e.what() << endl;
00216         d->errmsg = QString::fromUtf8( e.what() );
00217 
00218     }
00219     catch(...)
00220     {
00221         d->errmsg = i18n("Unknown error.");
00222     }
00223     return false;
00224 }
00225 
00226 //==================================================================================
00227 //Here we close the database connection
00228 bool pqxxSqlConnection::drv_closeDatabase()
00229 {
00230     KexiDBDrvDbg << "pqxxSqlConnection::drv_closeDatabase" << endl;
00231 //  if (isConnected())
00232 //  {
00233     delete d->pqxxsql;
00234     return true;
00235 //  }
00236 /* js: not needed, right? 
00237     else
00238     {
00239         d->errmsg = "Not connected to database backend";
00240         d->res = ERR_NO_CONNECTION;
00241     }
00242     return false;*/
00243 }
00244 
00245 //==================================================================================
00246 //Drops the given database
00247 bool pqxxSqlConnection::drv_dropDatabase( const QString &dbName )
00248 {
00249     KexiDBDrvDbg << "pqxxSqlConnection::drv_dropDatabase: " << dbName << endl;
00250 
00251     //FIXME Maybe should check that dbname is no the currentdb
00252     if (executeSQL("DROP DATABASE " + escapeName(dbName)))
00253         return true;
00254 
00255     return false;
00256 }
00257 
00258 //==================================================================================
00259 //Execute an SQL statement
00260 bool pqxxSqlConnection::drv_executeSQL( const QString& statement )
00261 {
00262 //  KexiDBDrvDbg << "pqxxSqlConnection::drv_executeSQL: " << statement << endl;
00263     bool ok = false;
00264 
00265     // Clear the last result information...
00266     delete d->res;
00267     d->res = 0;
00268 
00269 //  KexiDBDrvDbg << "About to try" << endl;
00270     try
00271     {
00272         //Create a transaction
00273         const bool implicityStarted = !m_trans;
00274         if (implicityStarted)
00275             (void)new pqxxTransactionData(this, true);
00276 
00277         //      m_trans = new pqxx::nontransaction(*m_pqxxsql);
00278 //      KexiDBDrvDbg << "About to execute" << endl;
00279         //Create a result object through the transaction
00280         d->res = new pqxx::result(m_trans->data->exec(statement.utf8()));
00281 //      KexiDBDrvDbg << "Executed" << endl;
00282         //Commit the transaction
00283         if (implicityStarted) {
00284             pqxxTransactionData *t = m_trans;
00285             drv_commitTransaction(t);
00286             delete t;
00287 //          m_trans = 0;
00288         }
00289 
00290         //If all went well then return true, errors picked up by the catch block
00291         ok = true;
00292     }
00293     catch (const std::exception &e)
00294     {
00295         //If an error ocurred then put the error description into _dbError
00296         d->errmsg = QString::fromUtf8( e.what() );
00297         KexiDBDrvDbg << "pqxxSqlConnection::drv_executeSQL:exception - " << e.what() << endl;
00298     }
00299     catch(...)
00300     {
00301         d->errmsg = i18n("Unknown error.");
00302     }
00303     //KexiDBDrvDbg << "EXECUTE SQL OK: OID was " << (d->res ? d->res->inserted_oid() : 0) << endl;
00304     return ok;
00305 }
00306 
00307 //==================================================================================
00308 //Return true if currently connected to a database, ignoring the m_is_connected falg.
00309 bool pqxxSqlConnection::drv_isDatabaseUsed() const
00310 {
00311     if (d->pqxxsql->is_open())
00312     {
00313         return true;
00314     }
00315     return false;
00316 }
00317 
00318 //==================================================================================
00319 //Return the oid of the last insert - only works if sql was insert of 1 row
00320 Q_ULLONG pqxxSqlConnection::drv_lastInsertRowID()
00321 {
00322     if (d->res)
00323     {
00324         pqxx::oid theOid = d->res->inserted_oid();
00325 
00326         if (theOid != pqxx::oid_none)
00327         {
00328             return (Q_ULLONG)theOid;
00329         }
00330         else
00331         {
00332             return 0;
00333         }
00334     }
00335     return 0;
00336 }
00337 
00338 //<queries taken from pqxxMigrate>
00339 bool pqxxSqlConnection::drv_containsTable( const QString &tableName )
00340 {
00341     bool success;
00342     return resultExists(QString("select 1 from pg_class where relkind='r' and relname LIKE %1")
00343         .arg(driver()->escapeString(tableName)), success) && success;
00344 }
00345 
00346 bool pqxxSqlConnection::drv_getTablesList( QStringList &list )
00347 {
00348     KexiDB::Cursor *cursor;
00349     m_sql = "select lower(relname) from pg_class where relkind='r'";
00350     if (!(cursor = executeQuery( m_sql ))) {
00351         KexiDBDrvWarn << "pqxxSqlConnection::drv_getTablesList(): !executeQuery()" << endl;
00352         return false;
00353     }
00354     list.clear();
00355     cursor->moveFirst();
00356     while (!cursor->eof() && !cursor->error()) {
00357         list += cursor->value(0).toString();
00358         cursor->moveNext();
00359     }
00360     if (cursor->error()) {
00361         deleteCursor(cursor);
00362         return false;
00363     }
00364     return deleteCursor(cursor);
00365 }
00366 //</taken from pqxxMigrate>
00367 
00368 TransactionData* pqxxSqlConnection::drv_beginTransaction()
00369 {
00370     return new pqxxTransactionData(this, false);
00371 }
00372 
00373 bool pqxxSqlConnection::drv_commitTransaction(TransactionData *tdata)
00374 {
00375     bool result = true;
00376     try {
00377         static_cast<pqxxTransactionData*>(tdata)->data->commit();
00378     }
00379     catch (const std::exception &e)
00380     {
00381         //If an error ocurred then put the error description into _dbError
00382         d->errmsg = QString::fromUtf8( e.what() );
00383         result = false;
00384     }
00385     catch (...) {
00387         setError();
00388         result = false;
00389     }
00390     if (m_trans == tdata)
00391         m_trans = 0;
00392     return result;
00393 }
00394 
00395 bool pqxxSqlConnection::drv_rollbackTransaction(TransactionData *tdata)
00396 {
00397     bool result = true;
00398     try {
00399         static_cast<pqxxTransactionData*>(tdata)->data->abort();
00400     }
00401     catch (const std::exception &e)
00402     {
00403         //If an error ocurred then put the error description into _dbError
00404         d->errmsg = QString::fromUtf8( e.what() );
00405         
00406         result = false;
00407     }
00408     catch (...) {
00409         d->errmsg = i18n("Unknown error.");
00410         result = false;
00411     }
00412     if (m_trans == tdata)
00413         m_trans = 0;
00414     return result;
00415 }
00416 
00417 int pqxxSqlConnection::serverResult()
00418 {
00419     return d->resultCode;
00420 }
00421 
00422 QString pqxxSqlConnection::serverResultName()
00423 {
00424     return QString::null;
00425 }
00426 
00427 void pqxxSqlConnection::drv_clearServerResult()
00428 {
00429     d->resultCode = 0;
00430 }
00431 
00432 QString pqxxSqlConnection::serverErrorMsg()
00433 {
00434     return d->errmsg;
00435 }
00436 
00437 PreparedStatement::Ptr pqxxSqlConnection::prepareStatement(PreparedStatement::StatementType type, 
00438     FieldList& fields)
00439 {
00440     return new pqxxPreparedStatement(type, *d, fields);
00441 }
00442 #include "pqxxconnection.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys