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 }
Generated on Sun Oct 3 2010 17:42:16 for EXTRAS-SOCI by Doxygen 1.7.1