/* This file is part of the KDE project Copyright (C) 2003 Adam Pigg This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "pqxxconnection.h" #include #include #include #include #include #include #include #include "pqxxpreparedstatement.h" #include "pqxxconnection_p.h" using namespace KexiDB; pqxxTransactionData::pqxxTransactionData(Connection *conn, bool nontransaction) : TransactionData(conn) { if (nontransaction) data = new pqxx::nontransaction(*static_cast(conn)->d->pqxxsql /* todo: add name? */); else data = new pqxx::transaction<>(*static_cast(conn)->d->pqxxsql /* todo: add name? */); if (!static_cast(conn)->m_trans) { static_cast(conn)->m_trans = this; } } pqxxTransactionData::~pqxxTransactionData() { if (static_cast(m_conn)->m_trans == this) { static_cast(m_conn)->m_trans = 0; } delete data; data = 0; } //================================================================================== pqxxSqlConnection::pqxxSqlConnection(Driver *driver, ConnectionData &conn_data) : Connection(driver,conn_data) , d( new pqxxSqlConnectionInternal(this) ) , m_trans(0) { } //================================================================================== //Do any tidying up before the object is deleted pqxxSqlConnection::~pqxxSqlConnection() { //delete m_trans; destroy(); delete d; } //================================================================================== //Return a new query based on a query statment Cursor* pqxxSqlConnection::prepareQuery( const TQString& statement, uint cursor_options) { Q_UNUSED(cursor_options); return new pqxxSqlCursor(this, statement, 1); //Always used buffered cursor } //================================================================================== //Return a new query based on a query object Cursor* pqxxSqlConnection::prepareQuery( QuerySchema& query, uint cursor_options) { Q_UNUSED(cursor_options); return new pqxxSqlCursor(this, query, 1);//Always used buffered cursor } //================================================================================== //Properly escaped a database object name TQString pqxxSqlConnection::escapeName(const TQString &name) const { return TQString("\"" + name + "\""); } //================================================================================== //Made this a noop //We tell kexi we are connected, but we wont actually connect until we use a database! bool pqxxSqlConnection::drv_connect(KexiDB::ServerVersionInfo& version) { KexiDBDrvDbg << "pqxxSqlConnection::drv_connect" << endl; version.clear(); d->version = &version; //remember for later... #ifdef __GNUC__ #warning pqxxSqlConnection::drv_connect implement setting version info when we drop libpqxx for libpq #endif return true; } //================================================================================== //Made this a noop //We tell kexi wehave disconnected, but it is actually handled by closeDatabse bool pqxxSqlConnection::drv_disconnect() { KexiDBDrvDbg << "pqxxSqlConnection::drv_disconnect: " << endl; return true; } //================================================================================== //Return a list of database names bool pqxxSqlConnection::drv_getDatabasesList( TQStringList &list ) { // KexiDBDrvDbg << "pqxxSqlConnection::drv_getDatabaseList" << endl; if (executeSQL("SELECT datname FROM pg_database WHERE datallowconn = TRUE")) { std::string N; for (pqxx::result::const_iterator c = d->res->begin(); c != d->res->end(); ++c) { // Read value of column 0 into a string N c[0].to(N); // Copy the result into the return list list << TQString::fromLatin1 (N.c_str()); } return true; } return false; } //================================================================================== //Create a new database bool pqxxSqlConnection::drv_createDatabase( const TQString &dbName ) { KexiDBDrvDbg << "pqxxSqlConnection::drv_createDatabase: " << dbName << endl; if (executeSQL("CREATE DATABASE " + escapeName(dbName))) return true; return false; } //================================================================================== //Use this as our connection instead of connect bool pqxxSqlConnection::drv_useDatabase( const TQString &dbName, bool *cancelled, MessageHandler* msgHandler ) { Q_UNUSED(cancelled); Q_UNUSED(msgHandler); KexiDBDrvDbg << "pqxxSqlConnection::drv_useDatabase: " << dbName << endl; TQString conninfo; TQString socket; TQStringList sockets; if (data()->hostName.isEmpty() || data()->hostName == "localhost") { if (data()->localSocketFileName.isEmpty()) { sockets.append("/tmp/.s.PGSQL.5432"); for(TQStringList::ConstIterator it = sockets.constBegin(); it != sockets.constEnd(); it++) { if(TQFile(*it).exists()) { socket = (*it); break; } } } else { socket=data()->localSocketFileName; //data()->fileName(); } } else { conninfo = "host='" + data()->hostName + "'"; } //Build up the connection string if (data()->port == 0) data()->port = 5432; conninfo += TQString::fromLatin1(" port='%1'").arg(data()->port); conninfo += TQString::fromLatin1(" dbname='%1'").arg(dbName); if (!data()->userName.isNull()) conninfo += TQString::fromLatin1(" user='%1'").arg(data()->userName); if (!data()->password.isNull()) conninfo += TQString::fromLatin1(" password='%1'").arg(data()->password); try { d->pqxxsql = new pqxx::connection( conninfo.latin1() ); if (d->version) { //! @todo set version using the connection pointer when we drop libpqxx for libpq } return true; } catch(const std::exception &e) { KexiDBDrvDbg << "pqxxSqlConnection::drv_useDatabase:exception - " << e.what() << endl; d->errmsg = TQString::fromUtf8( e.what() ); } catch(...) { d->errmsg = i18n("Unknown error."); } return false; } //================================================================================== //Here we close the database connection bool pqxxSqlConnection::drv_closeDatabase() { KexiDBDrvDbg << "pqxxSqlConnection::drv_closeDatabase" << endl; // if (isConnected()) // { delete d->pqxxsql; return true; // } /* js: not needed, right? else { d->errmsg = "Not connected to database backend"; d->res = ERR_NO_CONNECTION; } return false;*/ } //================================================================================== //Drops the given database bool pqxxSqlConnection::drv_dropDatabase( const TQString &dbName ) { KexiDBDrvDbg << "pqxxSqlConnection::drv_dropDatabase: " << dbName << endl; //FIXME Maybe should check that dbname is no the currentdb if (executeSQL("DROP DATABASE " + escapeName(dbName))) return true; return false; } //================================================================================== //Execute an SQL statement bool pqxxSqlConnection::drv_executeSQL( const TQString& statement ) { std::string temp = std::string(statement.utf8()); // Adding xmin result output for inserting rows bool isInserted = false; pqxx::result::const_iterator itRow; if (statement.find("INSERT INTO") == 0 || statement.find("UPDATE") == 0) { temp += " RETURNING xmin"; isInserted = true; } // KexiDBDrvDbg << "pqxxSqlConnection::drv_executeSQL: " << statement << endl; bool ok = false; // Clear the last result information... delete d->res; d->res = 0; // KexiDBDrvDbg << "About to try" << endl; try { //Create a transaction const bool implicityStarted = !m_trans; if (implicityStarted) (void)new pqxxTransactionData(this, true); // m_trans = new pqxx::nontransaction(*m_pqxxsql); // KexiDBDrvDbg << "About to execute" << endl; //Create a result object through the transaction d->res = new pqxx::result(m_trans->data->exec(temp)); // Get the xmin of the last inserted row if (isInserted) { itRow = d->res->begin(); itRow[0].to(temp); lastRowId = temp.c_str(); } // KexiDBDrvDbg << "Executed" << endl; //Commit the transaction if (implicityStarted) { pqxxTransactionData *t = m_trans; drv_commitTransaction(t); delete t; // m_trans = 0; } //If all went well then return true, errors picked up by the catch block ok = true; } catch(const pqxx::sql_error& sqlerr) { KexiDBDrvDbg << "pqxxSqlConnection::drv_executeSQL: sql_error exception - " << sqlerr.query().c_str() << endl; } catch (const pqxx::broken_connection& bcerr) { KexiDBDrvDbg << "pqxxSqlConnection::drv_executeSQL: broken_connection exception" << endl; } catch (const std::exception &e) { //If an error ocurred then put the error description into _dbError d->errmsg = TQString::fromUtf8( e.what() ); KexiDBDrvDbg << "pqxxSqlConnection::drv_executeSQL:exception - " << e.what() << endl; } catch(...) { d->errmsg = i18n("Unknown error."); } //KexiDBDrvDbg << "EXECUTE SQL OK: OID was " << (d->res ? d->res->inserted_oid() : 0) << endl; return ok; } //================================================================================== //Return true if currently connected to a database, ignoring the m_is_connected falg. bool pqxxSqlConnection::drv_isDatabaseUsed() const { if (d->pqxxsql->is_open()) { return true; } return false; } //================================================================================== //Return the oid of the last insert - only works if sql was insert of 1 row TQ_ULLONG pqxxSqlConnection::drv_lastInsertRowID() { if (d->res) { if (!lastRowId.isEmpty()) { return lastRowId.toULong(); } else { return 0; } } return 0; } // bool pqxxSqlConnection::drv_containsTable( const TQString &tableName ) { bool success; return resultExists(TQString("select 1 from pg_class where relkind='r' and relname LIKE %1") .arg(driver()->escapeString(tableName)), success) && success; } bool pqxxSqlConnection::drv_getTablesList( TQStringList &list ) { KexiDB::Cursor *cursor; m_sql = "select lower(relname) from pg_class where relkind='r'"; if (!(cursor = executeQuery( m_sql ))) { KexiDBDrvWarn << "pqxxSqlConnection::drv_getTablesList(): !executeQuery()" << endl; return false; } list.clear(); cursor->moveFirst(); while (!cursor->eof() && !cursor->error()) { list += cursor->value(0).toString(); cursor->moveNext(); } if (cursor->error()) { deleteCursor(cursor); return false; } return deleteCursor(cursor); } // TransactionData* pqxxSqlConnection::drv_beginTransaction() { return new pqxxTransactionData(this, false); } bool pqxxSqlConnection::drv_commitTransaction(TransactionData *tdata) { bool result = true; try { static_cast(tdata)->data->commit(); } catch (const std::exception &e) { //If an error ocurred then put the error description into _dbError d->errmsg = TQString::fromUtf8( e.what() ); result = false; } catch (...) { //! @todo setError(); result = false; } if (m_trans == tdata) m_trans = 0; return result; } bool pqxxSqlConnection::drv_rollbackTransaction(TransactionData *tdata) { bool result = true; try { static_cast(tdata)->data->abort(); } catch (const std::exception &e) { //If an error ocurred then put the error description into _dbError d->errmsg = TQString::fromUtf8( e.what() ); result = false; } catch (...) { d->errmsg = i18n("Unknown error."); result = false; } if (m_trans == tdata) m_trans = 0; return result; } int pqxxSqlConnection::serverResult() { return d->resultCode; } TQString pqxxSqlConnection::serverResultName() { return TQString(); } void pqxxSqlConnection::drv_clearServerResult() { d->resultCode = 0; } TQString pqxxSqlConnection::serverErrorMsg() { return d->errmsg; } PreparedStatement::Ptr pqxxSqlConnection::prepareStatement(PreparedStatement::StatementType type, FieldList& fields) { return new pqxxPreparedStatement(type, *d, fields); } #include "pqxxconnection.moc"