00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
00250 if(element.type>=U_TINY_N)
00251 element.type=Type(element.type-U_TINY_N);
00252
00253
00254 if(element.type<=U_BIGINT)
00255 {
00256 bindings[i].is_unsigned=1;
00257 element.type=Type(element.type+TINY);
00258 }
00259
00260
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
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
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 }