/* 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 "pqxxcursor.h" #include "pqxxconnection.h" #include "pqxxconnection_p.h" #include #include #include #include #include using namespace KexiDB; unsigned int pqxxSqlCursor_trans_num=0; //!< debug helper static TQByteArray pgsqlByteaToByteArray(const PQXX_FIELD_CLASS& r) { return KexiDB::pgsqlByteaToByteArray(r.c_str(), r.size()); } //================================================================================== //Constructor based on query statement pqxxSqlCursor::pqxxSqlCursor(KexiDB::Connection* conn, const TQString& statement, uint options): Cursor(conn,statement, options) { // KexiDBDrvDbg << "PQXXSQLCURSOR: constructor for query statement" << endl; my_conn = static_cast(conn)->d->pqxxsql; m_options = Buffered; m_res = 0; // m_tran = 0; m_implicityStarted = false; } //================================================================================== //Constructor base on query object pqxxSqlCursor::pqxxSqlCursor(Connection* conn, QuerySchema& query, uint options ) : Cursor( conn, query, options ) { // KexiDBDrvDbg << "PQXXSQLCURSOR: constructor for query schema" << endl; my_conn = static_cast(conn)->d->pqxxsql; m_options = Buffered; m_res = 0; // m_tran = 0; m_implicityStarted = false; } //================================================================================== //Destructor pqxxSqlCursor::~pqxxSqlCursor() { close(); } //================================================================================== //Create a cursor result set bool pqxxSqlCursor::drv_open() { // KexiDBDrvDbg << "pqxxSqlCursor::drv_open:" << m_sql << endl; if (!my_conn->is_open()) { //! @todo this check should be moved to Connection! when drv_prepareQuery() arrive //should never happen, but who knows setError(ERR_NO_CONNECTION,i18n("No connection for cursor open operation specified")); return false; } TQCString cur_name; //Set up a transaction try { //m_tran = new pqxx::work(*my_conn, "cursor_open"); cur_name.sprintf("cursor_transaction%d", pqxxSqlCursor_trans_num++); // m_tran = new pqxx::nontransaction(*my_conn, (const char*)cur_name); if (!((pqxxSqlConnection*)connection())->m_trans) { // my_conn->drv_beginTransaction(); // if (implicityStarted) (void)new pqxxTransactionData((pqxxSqlConnection*)connection(), true); m_implicityStarted = true; } m_res = new pqxx::result(((pqxxSqlConnection*)connection())->m_trans->data->exec(std::string(m_sql.utf8()))); ((pqxxSqlConnection*)connection()) ->drv_commitTransaction(((pqxxSqlConnection*)connection())->m_trans); // my_conn->m_trans->commit(); // KexiDBDrvDbg << "pqxxSqlCursor::drv_open: trans. committed: " << cur_name <columns() - (m_containsROWIDInfo ? 1 : 0); //js m_opened=true; m_afterLast=false; m_records_in_buf = m_res->size(); m_buffering_completed = true; return true; } catch (const std::exception &e) { setError(ERR_DB_SPECIFIC, TQString::fromUtf8( e.what()) ); KexiDBDrvWarn << "pqxxSqlCursor::drv_open:exception - " << TQString::fromUtf8( e.what() ) << endl; } catch(...) { setError(); } // delete m_tran; // m_tran = 0; if (m_implicityStarted) { delete ((pqxxSqlConnection*)connection())->m_trans; m_implicityStarted = false; } // KexiDBDrvDbg << "pqxxSqlCursor::drv_open: trans. rolled back! - " << cur_name <size() << " Current Position is " << (long)at() << endl; if(at() < m_res->size() && at() >=0) { m_result = FetchOK; } else if (at() >= m_res->size()) { m_result = FetchEnd; } else { m_result = FetchError; } } //================================================================================== //Check the current position is within boundaries void pqxxSqlCursor::drv_getPrevRecord() { // KexiDBDrvDbg << "pqxxSqlCursor::drv_getPrevRecord" << endl; if(at() < m_res->size() && at() >=0) { m_result = FetchOK; } else if (at() >= m_res->size()) { m_result = FetchEnd; } else { m_result = FetchError; } } //================================================================================== //Return the value for a given column for the current record TQVariant pqxxSqlCursor::value(uint pos) { if (pos < m_fieldCount) return pValue(pos); else return TQVariant(); } //================================================================================== //Return the value for a given column for the current record - Private const version TQVariant pqxxSqlCursor::pValue(uint pos)const { if (m_res->size() <= 0) { KexiDBDrvWarn << "pqxxSqlCursor::value - ERROR: result size not greater than 0" << endl; return TQVariant(); } if (pos>=(m_fieldCount+(m_containsROWIDInfo ? 1 : 0))) { // KexiDBDrvWarn << "pqxxSqlCursor::value - ERROR: requested position is greater than the number of fields" << endl; return TQVariant(); } KexiDB::Field *f = (m_fieldsExpanded && poscount(), m_fieldCount)) ? m_fieldsExpanded->at(pos)->field : 0; // KexiDBDrvDbg << "pqxxSqlCursor::value(" << pos << ")" << endl; //from most to least frequently used types: if (f) //We probably have a schema type query so can use kexi to determin the row type { if ((f->isIntegerType()) || (/*ROWID*/!f && m_containsROWIDInfo && pos==m_fieldCount)) { return (*m_res)[at()][pos].as(int()); } else if (f->isTextType()) { return TQString::fromUtf8((*m_res)[at()][pos].c_str()); //utf8? } else if (f->isFPNumericType()) { return (*m_res)[at()][pos].as(double()); } else if (f->type() == Field::Boolean ) { return TQString((*m_res)[at()][pos].c_str()).lower() == "t" ? TQVariant(true) : TQVariant(false); } else if (f->typeGroup() == Field::BLOBGroup) { // PQXX_FIELD_CLASS r = (*m_res)[at()][pos]; // kdDebug() << r.name() << ", " << r.c_str() << ", " << r.type() << ", " << r.size() << endl; return ::pgsqlByteaToByteArray((*m_res)[at()][pos]); } else { return pgsqlCStrToVariant((*m_res)[at()][pos]); } } else // We probably have a raw type query so use pqxx to determin the column type { return pgsqlCStrToVariant((*m_res)[at()][pos]); } return TQString::fromUtf8((*m_res)[at()][pos].c_str(), (*m_res)[at()][pos].size()); //utf8? } //================================================================================== //Return the current record as a char** //who'd have thought we'd be using char** in this day and age :o) const char** pqxxSqlCursor::rowData() const { // KexiDBDrvDbg << "pqxxSqlCursor::recordData" << endl; const char** row; row = (const char**)malloc(m_res->columns()+1); row[m_res->columns()] = NULL; if (at() >= 0 && at() < m_res->size()) { for(int i = 0; i < (int)m_res->columns(); i++) { row[i] = (char*)malloc(strlen((*m_res)[at()][i].c_str())+1); strcpy((char*)(*m_res)[at()][i].c_str(), row[i]); // KexiDBDrvDbg << row[i] << endl; } } else { KexiDBDrvWarn << "pqxxSqlCursor::recordData: m_at is invalid" << endl; } return row; } //================================================================================== //Store the current record in [data] void pqxxSqlCursor::storeCurrentRow(RowData &data) const { // KexiDBDrvDbg << "pqxxSqlCursor::storeCurrentRow: POSITION IS " << (long)m_at<< endl; if (m_res->size()<=0) return; const uint realCount = m_fieldCount + (m_containsROWIDInfo ? 1 : 0); data.resize(realCount); for( uint i=0; i