• Main Page
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

mysql.cpp

Go to the documentation of this file.
00001 
00002 /***************************************************************************
00003 * Copyright (C) 2007 Eddie Carle [eddie@erctech.org]                       *
00004 *                                                                          *
00005 * This file is part of fastcgi++.                                          *
00006 *                                                                          *
00007 * fastcgi++ is free software: you can redistribute it and/or modify it     *
00008 * under the terms of the GNU Lesser General Public License as  published   *
00009 * by the Free Software Foundation, either version 3 of the License, or (at *
00010 * your option) any later version.                                          *
00011 *                                                                          *
00012 * fastcgi++ is distributed in the hope that it will be useful, but WITHOUT *
00013 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    *
00014 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public     *
00015 * License for more details.                                                *
00016 *                                                                          *
00017 * You should have received a copy of the GNU Lesser General Public License *
00018 * along with fastcgi++.  If not, see <http://www.gnu.org/licenses/>.       *
00019 ****************************************************************************/
00020 
00021 
00022 #include <asql/mysql.hpp>
00023 #include <utf8_codecvt.hpp>
00024 
00025 void ASql::MySQL::Connection::connect(const char* host, const char* user, const char* passwd, const char* db, unsigned int port, const char* unix_socket, unsigned long client_flag, const char* const charset)
00026 {
00027    if(m_initialized)
00028    {
00029       for(unsigned int i=0; i<threads(); ++i)
00030       {
00031          mysql_stmt_close(foundRowsStatement[i]);
00032          mysql_close(&m_connection[i]);
00033       }
00034       m_initialized = false;
00035    }
00036 
00037    for(unsigned int i=0; i<threads(); ++i)
00038    {
00039       if(!mysql_init(&m_connection[i]))
00040          throw Error(&m_connection[i]);
00041 
00042       if(!mysql_real_connect(&m_connection[i], host, user, passwd, db, port, unix_socket, client_flag))
00043          throw Error(&m_connection[i]);
00044 
00045       if(mysql_set_character_set(&m_connection[i], charset))
00046          throw Error(&m_connection[i]);
00047       
00048       if(mysql_autocommit(&m_connection[i], 0))
00049          throw Error(&m_connection[i]);
00050       
00051       if(!(foundRowsStatement[i] = mysql_stmt_init(&m_connection[i])))
00052          throw Error(&m_connection[i]);
00053 
00054       if(mysql_stmt_prepare(foundRowsStatement[i], "SELECT FOUND_ROWS()", 19))
00055          throw Error(foundRowsStatement[i]);
00056 
00057       std::memset(&foundRowsBinding[i], 0, sizeof(MYSQL_BIND));
00058       foundRowsBinding[i].buffer_type = MYSQL_TYPE_LONGLONG;
00059       foundRowsBinding[i].is_unsigned = 1;
00060    }
00061 
00062    m_initialized = true;
00063 }
00064 
00065 ASql::MySQL::Connection::~Connection()
00066 {
00067    if(m_initialized)
00068    {
00069       for(unsigned int i=0; i<threads(); ++i)
00070       {
00071          mysql_stmt_close(foundRowsStatement[i]);
00072          mysql_close(&m_connection[i]);
00073       }
00074    }
00075 }
00076 
00077 void ASql::MySQL::Connection::getFoundRows(unsigned long long* const& rows, const unsigned int thread)
00078 {
00079    if(mysql_stmt_bind_param(foundRowsStatement[thread], 0))
00080       throw Error(foundRowsStatement[thread]);
00081    
00082    if(mysql_stmt_execute(foundRowsStatement[thread]))
00083       throw Error(foundRowsStatement[thread]);
00084 
00085    foundRowsBinding[thread].buffer = rows;
00086    if(mysql_stmt_bind_result(foundRowsStatement[thread], &foundRowsBinding[thread]))
00087       throw Error(foundRowsStatement[thread]);
00088 
00089    if(mysql_stmt_fetch(foundRowsStatement[thread]))
00090       throw Error(foundRowsStatement[thread]);
00091    mysql_stmt_free_result(foundRowsStatement[thread]);
00092    mysql_stmt_reset(foundRowsStatement[thread]);
00093 
00094 }
00095 
00096 void ASql::MySQL::Statement::init(const char* const& queryString, const size_t& queryLength, const Data::Set* const parameterSet, const Data::Set* const resultSet)
00097 {
00098    if(m_initialized)
00099    {
00100       for(unsigned int i=0; i<connection.threads(); ++i)
00101          mysql_stmt_close(stmt[i]);
00102       m_initialized = false;
00103    }
00104    
00105    for(unsigned int i=0; i<connection.threads(); ++i)
00106    {
00107       m_stop[i]=&ConnectionPar<Statement>::s_false;
00108       stmt[i]=mysql_stmt_init(&connection.connection(i));
00109       if(!stmt)
00110          throw Error(&connection.connection(i));
00111 
00112       if(mysql_stmt_prepare(stmt[i], queryString, queryLength))
00113          throw Error(stmt[i]);
00114 
00115       if(parameterSet) buildBindings(stmt[i], *parameterSet, paramsConversions[i], paramsBindings[i]);
00116       if(resultSet) buildBindings(stmt[i], *resultSet, resultsConversions[i], resultsBindings[i]);
00117    }
00118 
00119    m_initialized = true;
00120 }
00121 
00122 void ASql::MySQL::Statement::executeParameters(const Data::Set* const& parameters, const unsigned int thread)
00123 {
00124    if(parameters)
00125    {
00126       bindBindings(*const_cast<Data::Set*>(parameters), paramsConversions[thread], paramsBindings[thread]);
00127       for(Data::Conversions::iterator it=paramsConversions[thread].begin(); it!=paramsConversions[thread].end(); ++it)
00128          it->second->convertParam();
00129       if(mysql_stmt_bind_param(stmt[thread], paramsBindings[thread].get())!=0) throw Error(stmt[thread]);
00130    }
00131 
00132    if(mysql_stmt_execute(stmt[thread])!=0) throw Error(stmt[thread]);
00133 }
00134 
00135 bool ASql::MySQL::Statement::executeResult(Data::Set& row, const unsigned int thread)
00136 {
00137    bindBindings(row, resultsConversions[thread], resultsBindings[thread]);
00138    if(mysql_stmt_bind_result(stmt[thread], resultsBindings[thread].get())!=0) throw Error(stmt[thread]);
00139    switch (mysql_stmt_fetch(stmt[thread]))
00140    {
00141    case 1:
00142       throw Error(stmt[thread]);
00143    case MYSQL_NO_DATA:
00144       return false;
00145    default:
00146       for(Data::Conversions::iterator it=resultsConversions[thread].begin(); it!=resultsConversions[thread].end(); ++it)
00147          it->second->convertResult();
00148       return true;
00149    };
00150 }
00151 
00152 void ASql::MySQL::Statement::execute(const Data::Set* const parameters, Data::SetContainer* const results, unsigned long long int* const insertId, unsigned long long int* const rows, bool docommit, const unsigned int thread)
00153 {
00154    if(*m_stop[thread]) goto end;
00155    executeParameters(parameters, thread);
00156 
00157    if(results)
00158    {
00159       Data::SetContainer& res=*results;
00160 
00161       while(1)
00162       {{
00163          Data::Set& row=res.manufacture();
00164          bindBindings(row, resultsConversions[thread], resultsBindings[thread]);
00165          if(!executeResult(row, thread))
00166          {
00167             res.trim();
00168             break;
00169          }
00170          if(*m_stop[thread])
00171          {
00172             res.trim();
00173             goto end;
00174          }
00175       }}
00176 
00177       if(*m_stop[thread]) goto end;
00178       if(rows) connection.getFoundRows(rows, thread);
00179    }
00180    else
00181    {
00182       if(*m_stop[thread]) goto end;
00183       if(rows) *rows = mysql_stmt_affected_rows(stmt[thread]);
00184       if(*m_stop[thread]) goto end;
00185       if(insertId) *insertId = mysql_stmt_insert_id(stmt[thread]);
00186    }
00187 
00188 end:
00189    if(*m_stop[thread])
00190       connection.rollback(thread);
00191    else if(docommit)
00192       connection.commit(thread);
00193    mysql_stmt_free_result(stmt[thread]);
00194    mysql_stmt_reset(stmt[thread]);
00195 }
00196 
00197 bool ASql::MySQL::Statement::execute(const Data::Set* const parameters, Data::Set& results, bool docommit, const unsigned int thread)
00198 {
00199    bool retval(false);
00200    if(*m_stop[thread]) goto end;
00201    executeParameters(parameters, thread);
00202    if(*m_stop[thread]) goto end;
00203    retval=executeResult(results, thread);
00204 end:
00205    if(*m_stop[thread])
00206       connection.rollback(thread);
00207    else if(docommit)
00208       connection.commit(thread);
00209    mysql_stmt_free_result(stmt[thread]);
00210    mysql_stmt_reset(stmt[thread]);
00211    return retval;
00212 }
00213 
00214 void ASql::MySQL::Statement::execute(const Data::SetContainer& parameters, unsigned long long int* rows, bool docommit, const unsigned int thread)
00215 {
00216    if(rows) *rows = 0;
00217    
00218    for(const Data::Set* set=parameters.pull(); set!=0; set=parameters.pull())
00219    {
00220       if(*m_stop[thread]) break;
00221       executeParameters(set, thread);
00222       if(*m_stop[thread]) break;
00223       if(rows) *rows += mysql_stmt_affected_rows(stmt[thread]);
00224    }
00225    if(*m_stop[thread])
00226       connection.rollback(thread);
00227    else if(docommit)
00228       connection.commit(thread);
00229    mysql_stmt_free_result(stmt[thread]);
00230    mysql_stmt_reset(stmt[thread]);
00231 }
00232 
00233 void ASql::MySQL::Statement::buildBindings(MYSQL_STMT* const& stmt, const ASql::Data::Set& set, ASql::Data::Conversions& conversions, boost::scoped_array<MYSQL_BIND>& bindings)
00234 {
00235    using namespace Data;
00236    
00237    conversions.clear();
00238 
00239    const int& bindSize=set.numberOfSqlElements();
00240    if(!bindSize) return;
00241    bindings.reset(new MYSQL_BIND[bindSize]);
00242 
00243    std::memset(bindings.get(), 0, sizeof(MYSQL_BIND)*bindSize);
00244 
00245    for(int i=0; i<bindSize; ++i)
00246    {
00247       Index element(set.getSqlIndex(i));
00248 
00249       // Handle NULL
00250       if(element.type>=U_TINY_N)
00251          element.type=Type(element.type-U_TINY_N); // Make it the same type without the nullableness
00252 
00253       // Handle unsigned
00254       if(element.type<=U_BIGINT)
00255       {
00256          bindings[i].is_unsigned=1;
00257          element.type=Type(element.type+TINY);
00258       }
00259 
00260       // Start decoding values
00261       switch(element.type)
00262       {
00263          case TINY:
00264          {
00265             bindings[i].buffer_type=MYSQL_TYPE_TINY;
00266             break;
00267          }
00268 
00269          case SHORT:
00270          {
00271             bindings[i].buffer_type=MYSQL_TYPE_SHORT;
00272             break;
00273          }
00274          
00275          case INT:
00276          {
00277             bindings[i].buffer_type=MYSQL_TYPE_LONG;
00278             break;
00279          }
00280 
00281          case BIGINT:
00282          {
00283             bindings[i].buffer_type=MYSQL_TYPE_LONGLONG;
00284             break;
00285          }
00286 
00287          case FLOAT:
00288          {
00289             bindings[i].buffer_type=MYSQL_TYPE_FLOAT;
00290             break;
00291          }
00292 
00293          case DOUBLE:
00294          {
00295             bindings[i].buffer_type=MYSQL_TYPE_DOUBLE;
00296             break;
00297          }
00298 
00299          case DATE:
00300          {
00301             TypedConversion<Date>* conv = new TypedConversion<Date>;
00302             bindings[i].buffer = &conv->internal;
00303             bindings[i].buffer_type = MYSQL_TYPE_DATE;
00304             conversions[i].reset(conv);
00305             break;
00306          }
00307 
00308          case DATETIME:
00309          {
00310             TypedConversion<Datetime>* conv = new TypedConversion<Datetime>;
00311             bindings[i].buffer = &conv->internal;
00312             bindings[i].buffer_type = MYSQL_TYPE_DATETIME;
00313             conversions[i].reset(conv);
00314             break;
00315          }
00316 
00317          case TIME:
00318          {
00319             TypedConversion<Time>* conv = new TypedConversion<Time>;
00320             bindings[i].buffer = &conv->internal;
00321             bindings[i].buffer_type = MYSQL_TYPE_TIME;
00322             conversions[i].reset(conv);
00323             break;
00324          }
00325 
00326          case BLOB:
00327          {
00328             TypedConversion<Blob>* conv = new TypedConversion<Blob>(i, stmt, MYSQL_TYPE_BLOB, bindings[i].buffer);
00329             bindings[i].length = &conv->length;
00330             bindings[i].buffer_type = conv->bufferType;
00331             conversions[i].reset(conv);
00332             break;
00333          }
00334 
00335          case TEXT:
00336          {
00337             TypedConversion<Text>* conv = new TypedConversion<Text>(i, stmt, MYSQL_TYPE_STRING, bindings[i].buffer);
00338             bindings[i].length = &conv->length;
00339             bindings[i].buffer_type = conv->bufferType;
00340             conversions[i].reset(conv);
00341             break;
00342          }
00343 
00344          case WTEXT:
00345          {
00346             TypedConversion<Wtext>* conv = new TypedConversion<Wtext>(i, stmt, bindings[i].buffer);
00347 
00348             bindings[i].length = &conv->length;
00349             bindings[i].buffer_type = conv->bufferType;
00350             conversions[i].reset(conv);
00351             break;
00352          }
00353 
00354          case CHAR:
00355          case BINARY:
00356          {
00357             bindings[i].buffer_length = element.size;
00358             bindings[i].buffer_type = element.type==CHAR?MYSQL_TYPE_STRING:MYSQL_TYPE_BLOB;
00359             break;
00360          }
00361 
00362          default:
00363          {
00364             // Invalid element type, this shouldn't happen
00365             break;
00366          }
00367       }
00368    }
00369 }
00370 
00371 void ASql::MySQL::Statement::bindBindings(Data::Set& set, Data::Conversions& conversions, boost::scoped_array<MYSQL_BIND>& bindings)
00372 {
00373    int bindSize=set.numberOfSqlElements();
00374    for(int i=0; i<bindSize; ++i)
00375    {
00376       Data::Index element(set.getSqlIndex(i));
00377       if(element.type >= Data::U_TINY_N)
00378       {
00379          bindings[i].is_null = (my_bool*)&((Data::NullablePar*)element.data)->nullness;
00380          element.data = ((Data::NullablePar*)element.data)->getVoid();
00381       }
00382 
00383       Data::Conversions::iterator it=conversions.find(i);
00384       if(it==conversions.end())
00385          bindings[i].buffer=element.data;
00386       else
00387       {
00388          it->second->external=element.data;
00389          bindings[i].buffer=it->second->getPointer();
00390       }
00391    }
00392 }
00393 
00394 void ASql::MySQL::TypedConversion<ASql::Data::Datetime>::convertResult()
00395 {
00396    *(boost::posix_time::ptime*)external=boost::posix_time::ptime(boost::gregorian::date(internal.year, internal.month, internal.day), boost::posix_time::time_duration(internal.hour, internal.minute, internal.second));
00397 }
00398 
00399 void ASql::MySQL::TypedConversion<ASql::Data::Datetime>::convertParam()
00400 {
00401    std::memset(&internal, 0, sizeof(MYSQL_TIME));
00402    internal.year = ((boost::posix_time::ptime*)external)->date().year();
00403    internal.month = ((boost::posix_time::ptime*)external)->date().month();
00404    internal.day = ((boost::posix_time::ptime*)external)->date().day();
00405    internal.hour = ((boost::posix_time::ptime*)external)->time_of_day().hours();
00406    internal.minute = ((boost::posix_time::ptime*)external)->time_of_day().minutes();
00407    internal.second = ((boost::posix_time::ptime*)external)->time_of_day().seconds();
00408 }
00409 
00410 void ASql::MySQL::TypedConversion<ASql::Data::Date>::convertResult()
00411 {
00412    *(boost::gregorian::date*)external=boost::gregorian::date(internal.year, internal.month, internal.day);
00413 }
00414 
00415 void ASql::MySQL::TypedConversion<ASql::Data::Date>::convertParam()
00416 {
00417    std::memset(&internal, 0, sizeof(MYSQL_TIME));
00418    internal.year = ((boost::gregorian::date*)external)->year();
00419    internal.month = ((boost::gregorian::date*)external)->month();
00420    internal.day = ((boost::gregorian::date*)external)->day();
00421 }
00422 
00423 void ASql::MySQL::TypedConversion<ASql::Data::Time>::convertResult()
00424 {
00425    *(boost::posix_time::time_duration*)external = boost::posix_time::time_duration(internal.neg?internal.hour*-1:internal.hour, internal.minute, internal.second);
00426 }
00427 
00428 void ASql::MySQL::TypedConversion<ASql::Data::Time>::convertParam()
00429 {
00430    std::memset(&internal, 0, sizeof(MYSQL_TIME));
00431    internal.hour = std::abs(((boost::posix_time::time_duration*)external)->hours());
00432    internal.minute = std::abs(((boost::posix_time::time_duration*)external)->minutes());
00433    internal.second = std::abs(((boost::posix_time::time_duration*)external)->seconds());
00434    internal.neg = ((boost::posix_time::time_duration*)external)->hours() < 0 ? 1:0;
00435 }
00436 
00437 template void ASql::MySQL::TypedConversion<ASql::Data::Blob>::grabIt(ASql::Data::Blob& data);
00438 template void ASql::MySQL::TypedConversion<ASql::Data::Text>::grabIt(ASql::Data::Text& data);
00439 template<class T> void ASql::MySQL::TypedConversion<T>::grabIt(T& data)
00440 {
00441    if(data.size() != length) data.resize(length);
00442 
00443    if(length)
00444    {
00445       MYSQL_BIND bind;
00446       std::memset(&bind, 0, sizeof(bind));
00447       bind.buffer=&data[0];
00448       bind.buffer_length=length;
00449       bind.length=&length;
00450       bind.buffer_type=bufferType;
00451       if(mysql_stmt_fetch_column(statement, &bind, column, 0)!=0) throw Error(statement);
00452    }
00453 }
00454 
00455 template void ASql::MySQL::TypedConversion<ASql::Data::Blob>::convertParam();
00456 template void ASql::MySQL::TypedConversion<ASql::Data::Text>::convertParam();
00457 template<class T> void ASql::MySQL::TypedConversion<T>::convertParam()
00458 {
00459    T& data = *(T*)external;
00460 
00461    length = data.size();
00462    buffer = &data[0];
00463 }
00464 
00465 void ASql::MySQL::TypedConversion<ASql::Data::Wtext>::convertResult()
00466 {
00467    using namespace std;
00468    
00469    vector<char>& conversionBuffer = inputBuffer;
00470    grabIt(conversionBuffer);
00471 
00472    wstring& output = *(wstring*)external;
00473    output.resize(conversionBuffer.size());
00474 
00475    if(conversionBuffer.size())
00476    {
00477       wchar_t* it;
00478       const char* tmp;
00479       mbstate_t conversionState = mbstate_t();
00480       if(use_facet<codecvt<wchar_t, char, mbstate_t> >(locale(locale::classic(), new utf8CodeCvt::utf8_codecvt_facet)).in(conversionState, (const char*)&conversionBuffer.front(), (const char*)&conversionBuffer.front() + conversionBuffer.size(), tmp, &output[0], &output[0] + output.size(), it)!=codecvt_base::ok)
00481          throw ASql::Error(CodeConversionErrorMsg, -1);
00482       output.resize(it-&output[0]);
00483       conversionBuffer.clear();
00484    }
00485 }
00486 
00487 void ASql::MySQL::TypedConversion<ASql::Data::Wtext>::convertParam()
00488 {
00489    using namespace std;
00490 
00491    wstring& data = *(wstring*)external;
00492 
00493    inputBuffer.resize(data.size()*sizeof(wchar_t));
00494 
00495    if(inputBuffer.size())
00496    {
00497       const wchar_t* tmp;
00498       char* it;
00499       mbstate_t conversionState = mbstate_t();
00500       if(use_facet<codecvt<wchar_t, char, mbstate_t> >(locale(locale::classic(), new utf8CodeCvt::utf8_codecvt_facet)).out(conversionState, (const wchar_t*)&data[0], (const wchar_t*)&data[0] + data.size(), tmp, &inputBuffer.front(), &inputBuffer.front() + inputBuffer.size(), it)!=codecvt_base::ok) throw ASql::Error(CodeConversionErrorMsg, -1);
00501       inputBuffer.resize(it-&inputBuffer[0]);
00502    }
00503 
00504    buffer=&inputBuffer.front();
00505    length = inputBuffer.size();
00506 }
00507 
00508 
00509 //Instance the ConnectionPar functions
00510 template void ASql::ConnectionPar<ASql::MySQL::Statement>::start();
00511 template void ASql::ConnectionPar<ASql::MySQL::Statement>::terminate();
00512 template void ASql::ConnectionPar<ASql::MySQL::Statement>::intHandler(unsigned int id);
00513 template void ASql::ConnectionPar<ASql::MySQL::Statement>::queue(ASql::MySQL::Statement* const& statement, Query& query);
00514 template void ASql::ConnectionPar<ASql::MySQL::Statement>::queue(Transaction<ASql::MySQL::Statement>& transaction);
00515 template void ASql::Transaction<ASql::MySQL::Statement>::cancel();
00516 
00517 
00518 ASql::MySQL::Error::Error(MYSQL* mysql): ASql::Error(mysql_error(mysql), mysql_errno(mysql)) { }
00519 ASql::MySQL::Error::Error(MYSQL_STMT* stmt): ASql::Error(mysql_stmt_error(stmt), mysql_stmt_errno(stmt)) { }
00520 
00521 const char ASql::MySQL::CodeConversionErrorMsg[]="Error in code conversion to/from MySQL server.";
00522 
00523 ASql::MySQL::Statement::~Statement()
00524 {
00525    if(m_initialized)
00526    {
00527       for(unsigned int i=0; i<connection.threads(); ++i)
00528          mysql_stmt_close(stmt[i]);
00529    }
00530 }

Generated on Fri Jan 21 2011 12:29:32 for fastcgi++ by  doxygen 1.7.2