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