kexi

pqxxcursor.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 "pqxxcursor.h"
00021 #include "pqxxconnection.h"
00022 #include "pqxxconnection_p.h"
00023 
00024 #include <kexidb/error.h>
00025 #include <kexidb/global.h>
00026 
00027 #include <klocale.h>
00028 #include <kdebug.h>
00029 
00030 #include <migration/pqxx/pg_type.h>
00031 using namespace KexiDB;
00032 
00033 
00034 unsigned int pqxxSqlCursor_trans_num=0; 
00035 
00037 QByteArray processBinaryData(const pqxx::result::field r)
00038 {
00039     const int size = r.size();
00040     QByteArray array;
00041     int output=0;
00042     for (int pass=0; pass<2; pass++) {//2 passes to avoid allocating buffer twice:
00043                                         //  0: count #of chars; 1: copy data
00044         const char* s = r.c_str();
00045         const char* end = s + size;
00046         if (pass==1) {
00047             KexiDBDrvDbg << "processBinaryData(): real size == " << output << endl;
00048             array.resize(output);
00049             output=0;
00050         }
00051         for (int input=0; s < end; output++) {
00052     //      KexiDBDrvDbg<<(int)s[0]<<" "<<(int)s[1]<<" "<<(int)s[2]<<" "<<(int)s[3]<<" "<<(int)s[4]<<endl;
00053             if (s[0]=='\\' && (s+1)<end) {
00054                 //special cases as in http://www.postgresql.org/docs/8.1/interactive/datatype-binary.html
00055                 if (s[1]=='\'') {// \'
00056                     if (pass==1)
00057                         array[output] = '\'';
00058                     s+=2;
00059                 }
00060                 else if (s[1]=='\\') { // 2 backslashes 
00061                     if (pass==1)
00062                         array[output] = '\\';
00063                     s+=2;
00064                 }
00065                 else if ((input+3)<size) {// \\xyz where xyz are 3 octal digits
00066                     if (pass==1)
00067                         array[output] = char( (int(s[1]-'0')*8+int(s[2]-'0'))*8+int(s[3]-'0') );
00068                     s+=4;
00069                 }
00070                 else {
00071                     KexiDBDrvWarn << "processBinaryData(): no octal value after backslash" << endl;
00072                     s++;
00073                 }
00074             }
00075             else {
00076                 if (pass==1)
00077                     array[output] = s[0];
00078                 s++;
00079             }
00080     //      KexiDBDrvDbg<<output<<": "<<(int)array[output]<<endl;
00081         }
00082     }
00083     return array;
00084 }
00085 /*old
00086 QByteArray pqxxSqlCursor::processBinaryData(pqxx::binarystring *bs) const
00087 {
00088     QByteArray ba;
00089     kdDebug() << "pqxxSqlCursor::processBinaryData(): bs->data()=="<<(int)bs->data()[0]
00090      <<" "<<(int)bs->data()[1]<<" bs->size()=="<<bs->size()<<endl;
00091     ba.setRawData((const char *)(bs->data()), bs->size());
00092     ba.detach();
00093     return ba;
00094 }*/
00095 
00096 //==================================================================================
00097 //Constructor based on query statement
00098 pqxxSqlCursor::pqxxSqlCursor(KexiDB::Connection* conn, const QString& statement, uint options):
00099     Cursor(conn,statement, options)
00100 {
00101 //  KexiDBDrvDbg << "PQXXSQLCURSOR: constructor for query statement" << endl;
00102     my_conn = static_cast<pqxxSqlConnection*>(conn)->d->m_pqxxsql;
00103     m_options = Buffered;
00104     m_res = 0;
00105 //  m_tran = 0;
00106     m_implicityStarted = false;
00107 }
00108 
00109 //==================================================================================
00110 //Constructor base on query object
00111 pqxxSqlCursor::pqxxSqlCursor(Connection* conn, QuerySchema& query, uint options )
00112     : Cursor( conn, query, options )
00113 {
00114 //  KexiDBDrvDbg << "PQXXSQLCURSOR: constructor for query schema" << endl;
00115     my_conn = static_cast<pqxxSqlConnection*>(conn)->d->m_pqxxsql;
00116     m_options = Buffered;
00117     m_res = 0;
00118 //  m_tran = 0;
00119     m_implicityStarted = false;
00120 }
00121 
00122 //==================================================================================
00123 //Destructor
00124 pqxxSqlCursor::~pqxxSqlCursor()
00125 {
00126     close();
00127 }
00128 
00129 //==================================================================================
00130 //Create a cursor result set 
00131 bool pqxxSqlCursor::drv_open()
00132 {
00133 //  KexiDBDrvDbg << "pqxxSqlCursor::drv_open:" << m_sql << endl;
00134 
00135     if (!my_conn->is_open())
00136     {
00138         //should never happen, but who knows
00139         setError(ERR_NO_CONNECTION,i18n("No connection for cursor open operation specified"));
00140         return false;
00141     }
00142         
00143     QCString cur_name;
00144     //Set up a transaction
00145     try
00146     {
00147         //m_tran = new pqxx::work(*my_conn, "cursor_open");
00148         cur_name.sprintf("cursor_transaction%d", pqxxSqlCursor_trans_num++);
00149         
00150 //      m_tran = new pqxx::nontransaction(*my_conn, (const char*)cur_name);
00151         if (!((pqxxSqlConnection*)connection())->m_trans) {
00152 //          my_conn->drv_beginTransaction();
00153 //      if (implicityStarted)
00154             (void)new pqxxTransactionData((pqxxSqlConnection*)connection(), true);
00155             m_implicityStarted = true;
00156         }
00157 
00158         m_res = new pqxx::result(((pqxxSqlConnection*)connection())->m_trans->data->exec(m_sql.utf8()));
00159         ((pqxxSqlConnection*)connection())
00160             ->drv_commitTransaction(((pqxxSqlConnection*)connection())->m_trans);
00161 //      my_conn->m_trans->commit();
00162 //      KexiDBDrvDbg << "pqxxSqlCursor::drv_open: trans. commited: " << cur_name <<endl;
00163 
00164         //We should now be placed before the first row, if any
00165         m_fieldCount = m_res->columns() - (m_containsROWIDInfo ? 1 : 0);
00166 //js        m_opened=true;
00167         m_afterLast=false;
00168         m_records_in_buf = m_res->size();
00169         m_buffering_completed = true;
00170         return true;
00171     }
00172     catch (const std::exception &e)
00173     {
00174         setError(ERR_DB_SPECIFIC, QString::fromUtf8( e.what()) );
00175         KexiDBDrvWarn << "pqxxSqlCursor::drv_open:exception - " << QString::fromUtf8( e.what() ) << endl;
00176     }
00177     catch(...)
00178     {
00179         setError();
00180     }
00181 //  delete m_tran;
00182 //  m_tran = 0;
00183     if (m_implicityStarted) {
00184         delete ((pqxxSqlConnection*)connection())->m_trans;
00185         m_implicityStarted = false;
00186     }
00187 //  KexiDBDrvDbg << "pqxxSqlCursor::drv_open: trans. rolled back! - " << cur_name <<endl;
00188     return false;
00189 }
00190 
00191 //==================================================================================
00192 //Delete objects
00193 bool pqxxSqlCursor::drv_close()
00194 {
00195 //js    m_opened=false;
00196 
00197     delete m_res;
00198     m_res = 0;
00199 
00200 //  if (m_implicityStarted) {
00201 //      delete m_tran;
00202 //      m_tran = 0;
00203 //      m_implicityStarted = false;
00204 //  }
00205 
00206     return true;
00207 }
00208 
00209 //==================================================================================
00210 //Gets the next record...doesnt need to do much, just return fetchend if at end of result set
00211 void pqxxSqlCursor::drv_getNextRecord()
00212 {
00213 //  KexiDBDrvDbg << "pqxxSqlCursor::drv_getNextRecord, size is " <<m_res->size() << " Current Position is " << (long)at() << endl;
00214     if(at() < m_res->size() && at() >=0)
00215     {   
00216         m_result = FetchOK;
00217     }
00218     else if (at() >= m_res->size())
00219     {
00220         m_result = FetchEnd;
00221     }
00222     else
00223     {
00224         m_result = FetchError;
00225     }
00226 }
00227 
00228 //==================================================================================
00229 //Check the current position is within boundaries
00230 void pqxxSqlCursor::drv_getPrevRecord()
00231 {
00232 //  KexiDBDrvDbg << "pqxxSqlCursor::drv_getPrevRecord" << endl;
00233 
00234     if(at() < m_res->size() && at() >=0)
00235     {   
00236         m_result = FetchOK;
00237     }
00238     else if (at() >= m_res->size())
00239     {
00240         m_result = FetchEnd;
00241     }
00242     else
00243     {
00244         m_result = FetchError;
00245     }
00246 }
00247 
00248 //==================================================================================
00249 //Return the value for a given column for the current record
00250 QVariant pqxxSqlCursor::value(uint pos)
00251 {
00252     if (pos < m_fieldCount)
00253         return pValue(pos);
00254     else
00255         return QVariant();
00256 }
00257 
00258 //==================================================================================
00259 //Return the value for a given column for the current record - Private const version
00260 QVariant pqxxSqlCursor::pValue(uint pos)const
00261 {   
00262     if (m_res->size() <= 0)
00263     {
00264         KexiDBDrvWarn << "pqxxSqlCursor::value - ERROR: result size not greater than 0" << endl;
00265         return QVariant();
00266     }
00267 
00268     if (pos>=(m_fieldCount+(m_containsROWIDInfo ? 1 : 0)))
00269     {
00270 //      KexiDBDrvWarn << "pqxxSqlCursor::value - ERROR: requested position is greater than the number of fields" << endl;
00271         return QVariant();
00272     }
00273 
00274     KexiDB::Field *f = (m_fieldsExpanded && pos<m_fieldCount) ? m_fieldsExpanded->at(pos)->field : 0;
00275 
00276 //  KexiDBDrvDbg << "pqxxSqlCursor::value(" << pos << ")" << endl;
00277 
00278     //from most to least frequently used types:
00279     if (f) //We probably have a schema type query so can use kexi to determin the row type
00280     {
00281         if ((f->isIntegerType()) || (/*ROWID*/!f && m_containsROWIDInfo && pos==m_fieldCount))
00282         {
00283             return QVariant((*m_res)[at()][pos].as(int()));
00284         }
00285         else if (f->isTextType())
00286         {
00287             return QVariant((*m_res)[at()][pos].c_str());
00288         }
00289         else if (f->isFPNumericType())
00290         {
00291             return QVariant((*m_res)[at()][pos].as(double()));
00292         }
00293         else if (f->typeGroup() == Field::BLOBGroup)
00294         {
00295 //          pqxx::result::field r = (*m_res)[at()][pos];
00296 //          kdDebug() << r.name() << ", " << r.c_str() << ", " << r.type() << ", " << r.size() << endl;
00297             return QVariant(processBinaryData( (*m_res)[at()][pos] ));
00298         }
00299     }
00300     else // We probably have a raw type query so use pqxx to determin the column type
00301     {
00302         
00303         switch((*m_res)[at()][pos].type())
00304         {
00305             case BOOLOID:
00306                 return QVariant((*m_res)[at()][pos].c_str()); //TODO check formatting
00307             case INT2OID:
00308             case INT4OID:
00309             case INT8OID:
00310                 return QVariant((*m_res)[at()][pos].as(int()));
00311             case FLOAT4OID:
00312             case FLOAT8OID:
00313             case NUMERICOID:
00314                 return QVariant((*m_res)[at()][pos].as(double()));
00315             case DATEOID:
00316                 return QVariant((*m_res)[at()][pos].c_str()); //TODO check formatting
00317             case TIMEOID:
00318                 return QVariant((*m_res)[at()][pos].c_str()); //TODO check formatting
00319             case TIMESTAMPOID:
00320                 return QVariant((*m_res)[at()][pos].c_str()); //TODO check formatting
00321             case BYTEAOID:
00322                 return QVariant(processBinaryData( (*m_res)[at()][pos] ));
00323             case BPCHAROID:
00324             case VARCHAROID:
00325             case TEXTOID:
00326                 return QVariant((*m_res)[at()][pos].c_str());
00327             default:
00328                 return QVariant((*m_res)[at()][pos].c_str());
00329         }
00330         
00331     }
00332     
00333     //If all else fails just return a string
00334     return QVariant((*m_res)[at()][pos].c_str());
00335     
00336 }
00337 
00338 //==================================================================================
00339 //Return the current record as a char**
00340 //who'd have thought we'd be using char** in this day and age :o)
00341 const char** pqxxSqlCursor::rowData() const
00342 {
00343 //  KexiDBDrvDbg << "pqxxSqlCursor::recordData" << endl;
00344     
00345     const char** row;
00346     
00347     row = (const char**)malloc(m_res->columns()+1);
00348     row[m_res->columns()] = NULL;
00349     if (at() >= 0 && at() < m_res->size())
00350     {
00351         for(int i = 0; i < (int)m_res->columns(); i++)
00352         {
00353             row[i] = (char*)malloc(strlen((*m_res)[at()][i].c_str())+1);
00354             strcpy((char*)(*m_res)[at()][i].c_str(), row[i]);
00355 //          KexiDBDrvDbg << row[i] << endl;
00356         }
00357     }
00358     else
00359     {
00360         KexiDBDrvWarn << "pqxxSqlCursor::recordData: m_at is invalid" << endl;
00361     }
00362     return row;
00363 }
00364 
00365 //==================================================================================
00366 //Store the current record in [data]
00367 void pqxxSqlCursor::storeCurrentRow(RowData &data) const
00368 {
00369 //  KexiDBDrvDbg << "pqxxSqlCursor::storeCurrentRow: POSITION IS " << (long)m_at<< endl;
00370 
00371     if (m_res->size()<=0)
00372         return;
00373 
00374     const uint realCount = m_fieldCount + (m_containsROWIDInfo ? 1 : 0);
00375     data.resize(realCount);
00376 
00377     for( uint i=0; i<realCount; i++)
00378     {
00379         data[i] = pValue(i);
00380     }
00381 }
00382 
00383 //==================================================================================
00384 //
00385 void pqxxSqlCursor::drv_clearServerResult()
00386 {
00388 }
00389 
00390 //==================================================================================
00391 //Add the current record to the internal buffer
00392 //Implementation required but no need in this driver
00393 //Result set is a buffer so dont need another
00394 void pqxxSqlCursor::drv_appendCurrentRecordToBuffer()
00395 {
00396 
00397 }
00398 
00399 //==================================================================================
00400 //Move internal pointer to internal buffer +1
00401 //Implementation required but no need in this driver
00402 void pqxxSqlCursor::drv_bufferMovePointerNext()
00403 {
00404 
00405 }
00406 
00407 //==================================================================================
00408 //Move internal pointer to internal buffer -1
00409 //Implementation required but no need in this driver
00410 void pqxxSqlCursor::drv_bufferMovePointerPrev()
00411 {
00412 
00413 }
00414 
00415 //==================================================================================
00416 //Move internal pointer to internal buffer to N
00417 //Implementation required but no need in this driver
00418 void pqxxSqlCursor::drv_bufferMovePointerTo(Q_LLONG to)
00419 {
00420     Q_UNUSED(to);
00421 }
00422 
KDE Home | KDE Accessibility Home | Description of Access Keys