SOCI Logo Get SOCI at SourceForge.net. Fast, secure and Free Open Source software downloads

statement.cpp

Go to the documentation of this file.
00001 //
00002 // Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton
00003 // MySQL backend copyright (C) 2006 Pawel Aleksander Fedorynski
00004 // Distributed under the Boost Software License, Version 1.0.
00005 // (See accompanying file LICENSE_1_0.txt or copy at
00006 // http://www.boost.org/LICENSE_1_0.txt)
00007 //
00008 
00009 #define SOCI_MYSQL_SOURCE
00010 #include "soci-mysql.h"
00011 #include <cctype>
00012 #include <ciso646>
00013 //#include <iostream>
00014 
00015 #ifdef _MSC_VER
00016 #pragma warning(disable:4355)
00017 #endif
00018 
00019 using namespace soci;
00020 using namespace soci::details;
00021 using std::string;
00022 
00023 
00024 mysql_statement_backend::mysql_statement_backend(
00025     mysql_session_backend &session)
00026     : session_(session), result_(NULL), justDescribed_(false),
00027        hasIntoElements_(false), hasVectorIntoElements_(false),
00028        hasUseElements_(false), hasVectorUseElements_(false)
00029 {
00030 }
00031 
00032 void mysql_statement_backend::alloc()
00033 {
00034     // nothing to do here.
00035 }
00036 
00037 void mysql_statement_backend::clean_up()
00038 {
00039     if (result_ != NULL)
00040     {
00041         mysql_free_result(result_);
00042         result_ = NULL;
00043     }
00044 }
00045 
00046 void mysql_statement_backend::prepare(std::string const & query,
00047     statement_type /* eType */)
00048 {
00049     queryChunks_.clear();
00050     enum { eNormal, eInQuotes, eInName } state = eNormal;
00051 
00052     std::string name;
00053     queryChunks_.push_back("");
00054 
00055     for (std::string::const_iterator it = query.begin(), end = query.end();
00056          it != end; ++it)
00057     {
00058         switch (state)
00059         {
00060         case eNormal:
00061             if (*it == '\'')
00062             {
00063                 queryChunks_.back() += *it;
00064                 state = eInQuotes;
00065             }
00066             else if (*it == ':')
00067             {
00068                 state = eInName;
00069             }
00070             else // regular character, stay in the same state
00071             {
00072                 queryChunks_.back() += *it;
00073             }
00074             break;
00075         case eInQuotes:
00076             if (*it == '\'')
00077             {
00078                 queryChunks_.back() += *it;
00079                 state = eNormal;
00080             }
00081             else // regular quoted character
00082             {
00083                 queryChunks_.back() += *it;
00084             }
00085             break;
00086         case eInName:
00087             if (std::isalnum(*it) || *it == '_')
00088             {
00089                 name += *it;
00090             }
00091             else // end of name
00092             {
00093                 names_.push_back(name);
00094                 name.clear();
00095                 queryChunks_.push_back("");
00096                 queryChunks_.back() += *it;
00097                 state = eNormal;
00098             }
00099             break;
00100         }
00101     }
00102 
00103     if (state == eInName)
00104     {
00105         names_.push_back(name);
00106     }
00107 /*
00108   cerr << "Chunks: ";
00109   for (std::vector<std::string>::iterator i = queryChunks_.begin();
00110   i != queryChunks_.end(); ++i)
00111   {
00112   cerr << "\"" << *i << "\" ";
00113   }
00114   cerr << "\nNames: ";
00115   for (std::vector<std::string>::iterator i = names_.begin();
00116   i != names_.end(); ++i)
00117   {
00118   cerr << "\"" << *i << "\" ";
00119   }
00120   cerr << endl;
00121 */
00122 }
00123 
00124 statement_backend::exec_fetch_result
00125 mysql_statement_backend::execute(int number)
00126 {
00127     if (justDescribed_ == false)
00128     {
00129         clean_up();
00130         
00131         if (number > 1 && hasIntoElements_)
00132         {
00133              throw soci_error(
00134                   "Bulk use with single into elements is not supported.");
00135         }
00136         // number - size of vectors (into/use)
00137         // numberOfExecutions - number of loops to perform
00138         int numberOfExecutions = 1;
00139         if (number > 0)
00140         {
00141              numberOfExecutions = hasUseElements_ ? 1 : number;
00142         }
00143         
00144         std::string query;
00145         if (not useByPosBuffers_.empty() or not useByNameBuffers_.empty())
00146         {
00147             if (not useByPosBuffers_.empty() and not useByNameBuffers_.empty())
00148             {
00149                 throw soci_error(
00150                     "Binding for use elements must be either by position "
00151                     "or by name.");
00152             }
00153             for (int i = 0; i != numberOfExecutions; ++i)
00154             {
00155                 std::vector<char *> paramValues;
00156 
00157                 if (not useByPosBuffers_.empty())
00158                 {
00159                     // use elements bind by position
00160                     // the map of use buffers can be traversed
00161                     // in its natural order
00162 
00163                     for (UseByPosBuffersMap::iterator
00164                              it = useByPosBuffers_.begin(),
00165                              end = useByPosBuffers_.end();
00166                          it != end; ++it)
00167                     {
00168                         char **buffers = it->second;
00169                         //cerr<<"i: "<<i<<", buffers[i]: "<<buffers[i]<<endl;
00170                         paramValues.push_back(buffers[i]);
00171                     }
00172                 }
00173                 else
00174                 {
00175                     // use elements bind by name
00176 
00177                     for (std::vector<std::string>::iterator
00178                              it = names_.begin(), end = names_.end();
00179                          it != end; ++it)
00180                     {
00181                         UseByNameBuffersMap::iterator b
00182                             = useByNameBuffers_.find(*it);
00183                         if (b == useByNameBuffers_.end())
00184                         {
00185                             std::string msg(
00186                                 "Missing use element for bind by name (");
00187                             msg += *it;
00188                             msg += ").";
00189                             throw soci_error(msg);
00190                         }
00191                         char **buffers = b->second;
00192                         paramValues.push_back(buffers[i]);
00193                     }
00194                 }
00195                 //cerr << "queryChunks_.size(): "<<queryChunks_.size()<<endl;
00196                 //cerr << "paramValues.size(): "<<paramValues.size()<<endl;
00197                 if (queryChunks_.size() != paramValues.size()
00198                     and queryChunks_.size() != paramValues.size() + 1)
00199                 {
00200                     throw soci_error("Wrong number of parameters.");
00201                 }
00202 
00203                 std::vector<std::string>::const_iterator ci
00204                     = queryChunks_.begin();
00205                 for (std::vector<char*>::const_iterator
00206                          pi = paramValues.begin(), end = paramValues.end();
00207                      pi != end; ++ci, ++pi)
00208                 {
00209                     query += *ci;
00210                     query += *pi;
00211                 }
00212                 if (ci != queryChunks_.end())
00213                 {
00214                     query += *ci;
00215                 }
00216                 if (numberOfExecutions > 1)
00217                 {
00218                     // bulk operation
00219                     //std::cerr << "bulk operation:\n" << query << std::endl;
00220                     if (0 != mysql_real_query(session_.conn_, query.c_str(),
00221                             query.size()))
00222                     {
00223                         throw mysql_soci_error(mysql_error(session_.conn_),
00224                             mysql_errno(session_.conn_));
00225                     }
00226                     if (mysql_field_count(session_.conn_) != 0)
00227                     {
00228                         throw soci_error("The query shouldn't have returned"
00229                             " any data but it did.");
00230                     }
00231                     query.clear();
00232                 }
00233             }
00234             if (numberOfExecutions > 1)
00235             {
00236                 // bulk
00237                 return ef_no_data;
00238             }
00239         }
00240         else
00241         {
00242             query = queryChunks_.front();
00243         }
00244 
00245         //std::cerr << query << std::endl;
00246         if (0 != mysql_real_query(session_.conn_, query.c_str(),
00247                 query.size()))
00248         {
00249             throw mysql_soci_error(mysql_error(session_.conn_),
00250                 mysql_errno(session_.conn_));
00251         }
00252         result_ = mysql_store_result(session_.conn_);
00253         if (result_ == NULL and mysql_field_count(session_.conn_) != 0)
00254         {
00255             throw mysql_soci_error(mysql_error(session_.conn_),
00256                 mysql_errno(session_.conn_));
00257         }
00258         if (result_ != NULL)
00259         {
00260             // Cache the rows offsets to have random access to the rows later.
00261             // [mysql_data_seek() is O(n) so we don't want to use it].
00262             int numrows = mysql_num_rows(result_);
00263             resultRowOffsets_.resize(numrows);
00264             for (int i = 0; i < numrows; i++)
00265             {
00266                 resultRowOffsets_[i] = mysql_row_tell(result_);
00267                 mysql_fetch_row(result_);
00268             }
00269         }
00270     }
00271     else
00272     {
00273         justDescribed_ = false;
00274     }
00275 
00276     if (result_ != NULL)
00277     {
00278         currentRow_ = 0;
00279         rowsToConsume_ = 0;
00280 
00281         numberOfRows_ = mysql_num_rows(result_);
00282         if (numberOfRows_ == 0)
00283         {
00284             return ef_no_data;
00285         }
00286         else
00287         {
00288             if (number > 0)
00289             {
00290                 // prepare for the subsequent data consumption
00291                 return fetch(number);
00292             }
00293             else
00294             {
00295                 // execute(0) was meant to only perform the query
00296                 return ef_success;
00297             }
00298         }
00299     }
00300     else
00301     {
00302         // it was not a SELECT
00303         return ef_no_data;
00304     }
00305 }
00306 
00307 statement_backend::exec_fetch_result
00308 mysql_statement_backend::fetch(int number)
00309 {
00310     // Note: This function does not actually fetch anything from anywhere
00311     // - the data was already retrieved from the server in the execute()
00312     // function, and the actual consumption of this data will take place
00313     // in the postFetch functions, called for each into element.
00314     // Here, we only prepare for this to happen (to emulate "the Oracle way").
00315     
00316     // forward the "cursor" from the last fetch
00317     currentRow_ += rowsToConsume_;
00318 
00319     if (currentRow_ >= numberOfRows_)
00320     {
00321         // all rows were already consumed
00322         return ef_no_data;
00323     }
00324     else
00325     {
00326         if (currentRow_ + number > numberOfRows_)
00327         {
00328             rowsToConsume_ = numberOfRows_ - currentRow_;
00329 
00330             // this simulates the behaviour of Oracle
00331             // - when EOF is hit, we return ef_no_data even when there are
00332             // actually some rows fetched
00333             return ef_no_data;
00334         }
00335         else
00336         {
00337             rowsToConsume_ = number;
00338             return ef_success;
00339         }
00340     }
00341 }
00342 
00343 int mysql_statement_backend::get_number_of_rows()
00344 {
00345     return numberOfRows_ - currentRow_;
00346 }
00347 
00348 std::string mysql_statement_backend::rewrite_for_procedure_call(
00349     std::string const &query)
00350 {
00351     std::string newQuery("select ");
00352     newQuery += query;
00353     return newQuery;
00354 }
00355 
00356 int mysql_statement_backend::prepare_for_describe()
00357 {
00358     execute(1);
00359     justDescribed_ = true;
00360 
00361     int columns = mysql_field_count(session_.conn_);
00362     return columns;
00363 }
00364 
00365 void mysql_statement_backend::describe_column(int colNum,
00366     data_type & type, std::string & columnName)
00367 {
00368     int pos = colNum - 1;
00369     MYSQL_FIELD *field = mysql_fetch_field_direct(result_, pos);
00370     switch (field->type)
00371     {
00372     case FIELD_TYPE_CHAR:       //MYSQL_TYPE_TINY:
00373     case FIELD_TYPE_SHORT:      //MYSQL_TYPE_SHORT:
00374     case FIELD_TYPE_LONG:       //MYSQL_TYPE_LONG:
00375     case FIELD_TYPE_INT24:      //MYSQL_TYPE_INT24:
00376         type = dt_integer;
00377         break;
00378     case FIELD_TYPE_LONGLONG:   //MYSQL_TYPE_LONGLONG:
00379         type = dt_long_long;
00380         break;
00381     case FIELD_TYPE_FLOAT:      //MYSQL_TYPE_FLOAT:
00382     case FIELD_TYPE_DOUBLE:     //MYSQL_TYPE_DOUBLE:
00383     case FIELD_TYPE_DECIMAL:    //MYSQL_TYPE_DECIMAL:
00384     // Prior to MySQL v. 5.x there was no column type corresponding
00385     // to MYSQL_TYPE_NEWDECIMAL. However, MySQL server 5.x happily
00386     // sends field type number 246, no matter which version of libraries
00387     // the client is using.
00388     case 246:                   //MYSQL_TYPE_NEWDECIMAL:
00389         type = dt_double;
00390         break;
00391     case FIELD_TYPE_TIMESTAMP:  //MYSQL_TYPE_TIMESTAMP:
00392     case FIELD_TYPE_DATE:       //MYSQL_TYPE_DATE:
00393     case FIELD_TYPE_TIME:       //MYSQL_TYPE_TIME:
00394     case FIELD_TYPE_DATETIME:   //MYSQL_TYPE_DATETIME:
00395     case FIELD_TYPE_YEAR:       //MYSQL_TYPE_YEAR:
00396     case FIELD_TYPE_NEWDATE:    //MYSQL_TYPE_NEWDATE:
00397         type = dt_date;
00398         break;
00399 //  case MYSQL_TYPE_VARCHAR:
00400     case FIELD_TYPE_VAR_STRING: //MYSQL_TYPE_VAR_STRING:
00401     case FIELD_TYPE_STRING:     //MYSQL_TYPE_STRING:
00402     case FIELD_TYPE_BLOB:       // TEXT OR BLOB
00403         type = dt_string;
00404         break;
00405     default:
00406         //std::cerr << "field->type: " << field->type << std::endl;
00407         throw soci_error("Unknown data type.");
00408     }
00409     columnName = field->name;
00410 }
00411 
00412 mysql_standard_into_type_backend *
00413 mysql_statement_backend::make_into_type_backend()
00414 {
00415     hasIntoElements_ = true;
00416     return new mysql_standard_into_type_backend(*this);
00417 }
00418 
00419 mysql_standard_use_type_backend *
00420 mysql_statement_backend::make_use_type_backend()
00421 {
00422     hasUseElements_ = true;
00423     return new mysql_standard_use_type_backend(*this);
00424 }
00425 
00426 mysql_vector_into_type_backend *
00427 mysql_statement_backend::make_vector_into_type_backend()
00428 {
00429     hasVectorIntoElements_ = true;
00430     return new mysql_vector_into_type_backend(*this);
00431 }
00432 
00433 mysql_vector_use_type_backend *
00434 mysql_statement_backend::make_vector_use_type_backend()
00435 {
00436     hasVectorUseElements_ = true;
00437     return new mysql_vector_use_type_backend(*this);
00438 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
SourceForge Logo

Generated on Sun Oct 3 2010 17:42:16 for EXTRAS-SOCI by Doxygen 1.7.1