00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #ifndef _NO_COMMENT
00037 #define _NO_COMMENT // no #pragma comment(lib, ...) statements in .lib files
00038 #endif
00039
00040 #ifdef _MSC_VER
00041
00042 #if _MSC_VER>=1400 // VS2005 or higher
00043 #ifndef _CRT_SECURE_NO_DEPRECATE
00044 #define __STDC_WANT_SECURE_LIB__ 1
00045 #endif
00046
00047 #ifndef _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
00048 #define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
00049 #define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT 1
00050 #define _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES 1
00051 #endif
00052 #endif
00053
00054 #else // _MSC_VER
00055
00056 #define _snprintf snprintf
00057
00058 inline void _strupr(char* s)
00059 {
00060 for(; *s; ++s)
00061 *s = toupper(*s);
00062 }
00063
00064 #endif
00065
00066 #ifndef __STDC_WANT_SECURE_LIB__
00067 #define sscanf_s sscanf // be careful: Format strings of sscanf_s() and sscanf() are only identically, if there are not used string buffers as return values!
00068 #define _strupr_s(n, l) _strupr(n)
00069 #endif
00070
00071 #include "mysqlpl.h"
00072
00073 #include <sstream>
00074 #include <assert.h>
00075
00076
00077 namespace MySqlPL {
00078
00079
00080 MSqlException::MSqlException(MYSQL* db, const char* file, int line)
00081 {
00082 char buffer[2048];
00083
00084 _sql_error_code = mysql_errno(db);
00085 const char* error_str = mysql_error(db);
00086
00087 #if MYSQLPL_PREP_STMT
00088 const char* state_str = mysql_sqlstate(db);
00089 sprintf(buffer, "MySQL error %u - SQLSTATE %s: %s", _sql_error_code, state_str, error_str);
00090 _sqlstate = state_str;
00091 #else
00092 sprintf(buffer, "MySQL error %u: %s", _sql_error_code, error_str);
00093 #endif
00094
00095 _msg = _strdup((const char*)buffer);
00096
00097 _file = file;
00098 _line = line;
00099 }
00100
00101 #if MYSQLPL_PREP_STMT
00102 MSqlException::MSqlException(MYSQL_STMT* s, const char* file, int line)
00103 {
00104 char buffer[2048];
00105
00106 _sql_error_code = mysql_stmt_errno(s);
00107 const char* error_str = mysql_stmt_error(s);
00108
00109 const char* state_str = mysql_stmt_sqlstate(s);
00110 sprintf(buffer, "MySQL error %u - SQLSTATE %s: %s", _sql_error_code, state_str, error_str);
00111 _sqlstate = state_str;
00112
00113 _msg = _strdup(buffer);
00114
00115 _file = file;
00116 _line = line;
00117 }
00118
00119 MSqlException::MSqlException(MYSQL_STMT* s, SqlStatement& stmt, const char* file, int line)
00120 {
00121 char buffer[2048];
00122
00123 _sql_error_code = mysql_stmt_errno(s);
00124 const char* error_str = mysql_stmt_error(s);
00125 const char* state_str = mysql_stmt_sqlstate(s);
00126
00127 sprintf(buffer, "MySQL error %u - SQLSTATE %s: %s", _sql_error_code, state_str, error_str);
00128
00129 _last_sql = stmt.get_last_sql();
00130
00131 if (!_last_sql.empty()) {
00132 const char* sql = _last_sql.c_str();
00133
00134 _snprintf(buffer, sizeof(buffer)-1, "%s\nlast SQL statement:\n%s\n", buffer, sql);
00135 }
00136
00137 _msg = _strdup((const char*)buffer);
00138 _sqlstate = state_str;
00139
00140 _file = file;
00141 _line = line;
00142 }
00143 #endif
00144
00145 MSqlException::MSqlException(const char* msg, const char* file, int line)
00146 {
00147 _msg = _strdup(msg);
00148 _file = file;
00149 _line = line;
00150 _sql_error_code = 0;
00151 }
00152
00153
00154 void MSqlConnection::connect(const char* username, const char* password, const char* db_name,
00155 const char* host, unsigned port, unsigned long clientflag)
00156 {
00157 _connected = false;
00158
00159 _db = mysql_real_connect(_env, host, username, password, db_name, port, NULL, clientflag);
00160
00161 if (!_db)
00162 THROW_MYSQLPL_EXCEPTION(_env);
00163
00164 _connected = true;
00165
00166 #if MYSQLPL_PREP_STMT
00167
00168 my_bool res = mysql_autocommit(_db, 0);
00169 mysqlpl_check_error(_env, res);
00170 #else
00171 SqlStatement(*this).execute("set autocommit=0");
00172 #endif
00173 }
00174
00175
00176 #if MYSQLPL_PREP_STMT
00177 unsigned MSqlConnection::get_warnings_count() const
00178 {
00179 unsigned res = mysql_warning_count(_db);
00180
00181 if (res == -1)
00182 THROW_MYSQLPL_EXCEPTION(_db);
00183
00184 return res;
00185 }
00186
00187 std::string MSqlConnection::get_info() const
00188 {
00189 const char* info = mysql_info(_db);
00190
00191 if (!info)
00192 THROW_MYSQLPL_EXCEPTION(_db);
00193
00194 return info;
00195 }
00196 #else
00197 const char* MSqlConnection::get_info() const
00198 {
00199 return mysql_info(_db);
00200 }
00201 #endif
00202
00203
00204 #if MYSQLPL_PREP_STMT
00205
00206 std::string SqlInt::str() const
00207 {
00208 if (_ind.is_null())
00209 return "";
00210
00211 char buffer[16];
00212
00213 sprintf(buffer, "%d", _value);
00214
00215 return buffer;
00216 }
00217
00218 std::string SqlInteger::str() const
00219 {
00220 if (_ind.is_null())
00221 return "";
00222
00223 char buffer[32];
00224
00225 sprintf(buffer, "%I64d", _value);
00226
00227 return buffer;
00228 }
00229
00230 SqlNumber::SqlNumber(const char* s)
00231 {
00232 if (sscanf_s(s, "%lf", &_value) == 1)
00233 _ind.set();
00234 }
00235
00236 std::string SqlNumber::str() const
00237 {
00238 if (_ind.is_null())
00239 return "";
00240
00241 char buffer[64];
00242
00243 sprintf(buffer, "%f", _value);
00244
00245 return buffer;
00246 }
00247
00248
00249 SqlDate::SqlDate(const char* str, enum_mysql_timestamp_type type)
00250 {
00251 while(str && isspace(*str))
00252 ++str;
00253
00254 if (str && *str) {
00255 hour = 0;
00256 minute = 0;
00257 second = 0;
00258 second_part = 0;
00259
00260 switch(type) {
00261 case MYSQL_TIMESTAMP_DATE:
00262 if (sscanf_s(str, "%04d-%02d-%02d",
00263 &year, &month, &day) != 3)
00264 if (sscanf_s(str, "%02d.%02d.%04d",
00265 &day, &month, &year) != 3)
00266 THROW_MYSQLPL_EXCEPTION("date conversion error");
00267 break;
00268
00269 case MYSQL_TIMESTAMP_TIME:
00270 if (sscanf_s(str, "%02d:%02d:%02d.%06d",
00271 &hour, &minute, &second, &second_part) < 3)
00272 THROW_MYSQLPL_EXCEPTION("time conversion error");
00273 break;
00274
00275
00276 default:
00277 if (sscanf_s(str, "%04d-%02d-%02d %02d:%02d:%02d.%06d",
00278 &year, &month, &day, &hour, &minute, &second, &second_part) < 6) {
00279 second = 0;
00280 if (sscanf_s(str, "%02d.%02d.%04d %02d:%02d:%02d.%06d",
00281 &day, &month, &year, &hour, &minute, &second, &second_part) < 3)
00282 THROW_MYSQLPL_EXCEPTION("datetime conversion error");
00283 }
00284 }
00285
00286 super::time_type = type;
00287
00288 _ind.set();
00289 } else {
00290 super::time_type = MYSQL_TIMESTAMP_NONE;
00291
00292 _ind.clear();
00293 }
00294 }
00295
00296 SqlDate::SqlDate(const SqlDate& other, enum_mysql_timestamp_type type)
00297 : SqlValue(other)
00298 {
00299 memcpy(this, &other, sizeof(MYSQL_TIME));
00300
00301 if (other.time_type != type) {
00302 if (type==MYSQL_TIMESTAMP_NONE || type==MYSQL_TIMESTAMP_ERROR)
00303 super::time_type = type;
00304 else {
00305 if (other.time_type == MYSQL_TIMESTAMP_DATE) {
00306 if (type == MYSQL_TIMESTAMP_TIME)
00307 THROW_MYSQLPL_EXCEPTION("no conversion from time to date");
00308
00309 super::hour = 0;
00310 super::minute = 0;
00311 super::second = 0;
00312 } else if (other.time_type == MYSQL_TIMESTAMP_TIME) {
00313 if (type == MYSQL_TIMESTAMP_DATE)
00314 THROW_MYSQLPL_EXCEPTION("no conversion from date to time");
00315
00316 super::year = 0;
00317 super::month = 0;
00318 super::day = 0;
00319 }
00320
00321 super::time_type = type;
00322 }
00323 }
00324 }
00325
00326
00327 std::string SqlDate::str() const
00328 {
00329 if (_ind.is_not_null()) {
00330 char buffer[40];
00331
00332 switch(time_type) {
00333 case MYSQL_TIMESTAMP_DATE:
00334 sprintf(buffer, "%04d-%02d-%02d",
00335 year, month, day);
00336 break;
00337
00338 case MYSQL_TIMESTAMP_TIME:
00339 sprintf(buffer, "%02d:%02d:%02d.%06d",
00340 hour, minute, second, second_part);
00341 break;
00342
00343
00344 default:
00345 sprintf(buffer, "%04d-%02d-%02d %02d:%02d:%02d.%06d",
00346 year, month, day, hour, minute, second, second_part);
00347 }
00348
00349 return buffer;
00350 } else
00351 return std::string();
00352 }
00353
00354
00355 std::string MSqlBlob::str() const
00356 {
00357 if (_ind.is_not_null()) {
00358 return std::string((char*)_ptr, _len);
00359 } else
00360 return std::string();
00361 }
00362
00363 #endif // MYSQLPL_PREP_STMT
00364
00365
00366 std::string ColumnType::get_type_str(bool show_null) const
00367 {
00368 std::ostringstream str;
00369
00370 switch(_data_type) {
00371 case MYSQL_TYPE_STRING:
00372 str << "char(" << _width << ")";
00373 break;
00374
00375 case MYSQL_TYPE_VAR_STRING:
00376 str << "varchar(" << _width << ")";
00377 break;
00378
00379 case MYSQL_TYPE_DECIMAL:
00380 if (_width == 0)
00381 str << "decimal";
00382 else
00383 str << "decimal(" << (_width-1) << "," << _decimals << ")";
00384 break;
00385
00386 #if MYSQL_VERSION_ID>=50000
00387 case MYSQL_TYPE_NEWDECIMAL:
00388 if (_width == 0)
00389 str << "newdecimal";
00390 else
00391 str << "newdecimal(" << (_width-1) << "," << _decimals << ")";
00392 break;
00393 #endif
00394
00395 case MYSQL_TYPE_TINY:
00396 str << "tinyint(" << _width << ")";
00397 break;
00398 case MYSQL_TYPE_SHORT:
00399 str << "shortint(" << _width << ")";
00400 break;
00401 case MYSQL_TYPE_LONG:
00402 str << "int(" << _width << ")";
00403 break;
00404 case MYSQL_TYPE_LONGLONG:
00405 str << "bigint(" << _width << ")";
00406 break;
00407
00408 #ifdef FIELD_TYPE_ENUM
00409 case FIELD_TYPE_ENUM:
00410 str << "enum";
00411 break;
00412 #endif
00413
00414 case MYSQL_TYPE_FLOAT:
00415 if (_width == 12)
00416 str << "float";
00417 else
00418 str << "float(" << _width << "," << _decimals << ")";
00419 break;
00420
00421 case MYSQL_TYPE_DOUBLE:
00422 if (_width == 22)
00423 str << "double";
00424 else
00425 str << "double(" << _width << "," << _decimals << ")";
00426 break;
00427
00428 case MYSQL_TYPE_DATE:
00429 str << "date";
00430 break;
00431 #if MYSQL_VERSION_ID>=50000
00432 case MYSQL_TYPE_NEWDATE:
00433 str << "newdate";
00434 break;
00435 #endif
00436 case MYSQL_TYPE_DATETIME:
00437 str << "datetime";
00438 break;
00439 case MYSQL_TYPE_TIME:
00440 str << "time";
00441 break;
00442 case MYSQL_TYPE_YEAR:
00443 str << "year(" << _width << ")";
00444 break;
00445
00446 case MYSQL_TYPE_TINY_BLOB:
00447 if (_flags & BINARY_FLAG)
00448 str << "tinyblob";
00449 else
00450 str << "tinytext";
00451 break;
00452 case MYSQL_TYPE_MEDIUM_BLOB:
00453 if (_flags & BINARY_FLAG)
00454 str << "mediumblob";
00455 else
00456 str << "mediumtext";
00457 break;
00458 case MYSQL_TYPE_LONG_BLOB:
00459 if (_flags & BINARY_FLAG)
00460 str << "longblob";
00461 else
00462 str << "longtext";
00463 break;
00464 case MYSQL_TYPE_BLOB:
00465 if (_flags & BINARY_FLAG)
00466 str << "blob";
00467 else
00468 str << "text";
00469 break;
00470
00471 default:
00472 str << "unknown";
00473 }
00474
00475 if (show_null)
00476 if (IS_NOT_NULL(_flags))
00477 str << " not null";
00478 else
00479 str << " null";
00480
00481 return str.str();
00482 }
00483
00484
00485 SqlStatement::SqlStatement(MSqlConnection& conn)
00486 : _db(conn),
00487 _state(UNINITIALIZED),
00488 _res(NULL),
00489 _stmt_type(-1),
00490 _result(NULL)
00491 {
00492 #if MYSQLPL_PREP_STMT
00493 _stmt = mysql_stmt_init(_db);
00494 #endif
00495 }
00496
00497 SqlStatement::SqlStatement(MSqlConnection& conn, const std::string& sql)
00498 : _db(conn),
00499 _state(UNINITIALIZED),
00500 _res(NULL),
00501 _stmt_type(-1),
00502 _result(NULL)
00503 {
00504 #if MYSQLPL_PREP_STMT
00505 _stmt = mysql_stmt_init(_db);
00506 #endif
00507
00508 prepare(sql);
00509 }
00510
00511 SqlStatement::SqlStatement(MSqlConnection& conn, const SqlQuery& query)
00512 : _db(conn),
00513 _state(UNINITIALIZED),
00514 _res(NULL),
00515 _stmt_type(-1),
00516 _result(NULL)
00517 {
00518 #if MYSQLPL_PREP_STMT
00519 _stmt = mysql_stmt_init(_db);
00520 #endif
00521
00522 execute(query);
00523 }
00524
00525 SqlStatement::~SqlStatement()
00526 {
00527 #if MYSQLPL_PREP_STMT
00528 if (_stmt)
00529 mysql_stmt_close(_stmt);
00530 #else
00531 if (_result)
00532 mysql_free_result(_result);
00533 #endif
00534 }
00535
00536
00537 void SqlStatement::prepare(const std::string& sql)
00538 {
00539 delete _res;
00540 _res = NULL;
00541
00542 _last_sql = sql;
00543
00544 #if MYSQLPL_PREP_STMT
00545 int res = mysql_stmt_prepare(_stmt, sql.c_str(), sql.length());
00546 mysqlpl_check_error(_stmt, res);
00547
00548 _state = PREPARED;
00549 #else
00550 _sql = sql;
00551
00552 if (_result)
00553 mysql_free_result(_result);
00554 #endif
00555
00556 _stmt_type = -1;
00557 _result = NULL;
00558
00559 _param_bind_vector.clear();
00560 }
00561
00562
00563 LONGLONG SqlStatement::get_row_count() const
00564 {
00565 #if MYSQLPL_PREP_STMT
00566 if (is_select()) {
00567 if (mysql_stmt_store_result(_stmt))
00568 THROW_MYSQLPL_EXCEPTION(_stmt);
00569 }
00570
00571 LONGLONG res = mysql_stmt_affected_rows(_stmt);
00572
00573
00574
00575
00576
00577 if (res == -1)
00578 THROW_MYSQLPL_EXCEPTION(_stmt);
00579 #else
00580 int res = mysql_affected_rows(_db);
00581
00582 if (res == -1)
00583 THROW_MYSQLPL_EXCEPTION(_db);
00584 #endif
00585
00586 return res;
00587 }
00588
00589
00590 #if MYSQLPL_PREP_STMT//@@
00591 void SqlStatement::bind_result(MYSQL_BIND* bind_array)
00592 {
00593 my_bool res = mysql_stmt_bind_result(_stmt, bind_array);
00594
00595 mysqlpl_check_error(_stmt, res);
00596
00597 if (_state & EXECUTED) {
00598 delete _res;
00599 _res = NULL;
00600
00601 _state = (_state|RESULT_BOUND) & ~FETCHED;
00602 } else
00603 _state |= RESULT_BOUND;
00604 }
00605 #endif
00606
00607
00608 void SqlStatement::execute()
00609 {
00610 #if MYSQLPL_PREP_STMT
00611 if (!(_state & RESULT_BOUND)) {
00612
00613 if (is_select()) {
00614 _result_bind_vector.resize(get_column_count());
00615
00616
00617 assert(!_res);
00618 _res = new SqlResult(*this);
00619 }
00620 }
00621
00622 MSqlBindVector param_mbind_vector;
00623
00624
00625 if (!_param_bind_vector.empty()) {
00626
00627 param_mbind_vector.resize(_param_bind_vector.size());
00628
00629 MSqlBindVector::iterator out = param_mbind_vector.begin();
00630 for(BindVector::const_iterator in=_param_bind_vector.begin(); in!=_param_bind_vector.end(); ++in,++out)
00631 mysql_bind_param(*in, *out);
00632
00633 my_bool res = mysql_stmt_bind_param(_stmt, ¶m_mbind_vector[0]);
00634
00635 mysqlpl_check_error(_stmt, res);
00636 }
00637
00638 int res = mysql_stmt_execute(_stmt);
00639
00640 mysqlpl_check_error(_stmt, res);
00641 #else
00642 int res = mysql_query(_db, create_bound_sql().c_str());
00643
00644 mysqlpl_check_error(_db, res);
00645
00646 if (_result)
00647 mysql_free_result(_result);
00648
00649 _result = mysql_store_result(_db);
00650
00651 if (_result) {
00652
00653 if (_res)
00654 delete _res;
00655
00656 _res = new SqlResult(*this);
00657 }
00658 #endif
00659
00660 _state = (_state|EXECUTED) & ~FETCHED;
00661 }
00662
00663 #if !MYSQLPL_PREP_STMT
00664 static const char* next_bind_variable(const char* p)
00665 {
00666 for(; *p; ++p)
00667 if (*p == '?')
00668 return p;
00669
00670 else if (*p == '\'') {
00671 while(*++p)
00672 if (*p=='\'' && *++p!='\'')
00673 break;
00674 }
00675
00676 return NULL;
00677 }
00678
00679
00680 std::string SqlStatement::create_bound_sql() const
00681 {
00682 const char* p = _sql.c_str();
00683 std::string sql;
00684 char buffer[64];
00685
00686 for(BindVector::const_iterator it=_param_bind_vector.begin(); it!=_param_bind_vector.end(); ++it) {
00687 const char* q = next_bind_variable(p);
00688
00689 if (!q)
00690 THROW_MYSQLPL_EXCEPTION("too much bind values");
00691
00692 sql.append(p, q-p);
00693
00694 const BindPar& value = *it;
00695
00696 switch(value._type) {
00697 case MYSQL_TYPE_VAR_STRING: {
00698 sql.append("'");
00699
00700
00701 const char* p = (const char*)value._ptr;
00702
00703 for(int i=0; i<value._len; ++i,++p)
00704 if (*p == '\'')
00705 sql.append("''");
00706 else
00707 sql.append(p, 1);
00708
00709 sql.append("'");
00710 break;}
00711
00712 case MYSQL_TYPE_LONG:
00713 sql.append(buffer, sprintf(buffer,"%d",*(int*)value._ptr));
00714 break;
00715
00716 case MYSQL_TYPE_DOUBLE:
00717 sql.append(buffer, sprintf(buffer,"%f",*(double*)value._ptr));
00718 break;
00719
00720 default:
00721 THROW_MYSQLPL_EXCEPTION("unsupported datatype");
00722 }
00723
00724 p = q + 1;
00725 }
00726
00727 if (next_bind_variable(p))
00728 THROW_MYSQLPL_EXCEPTION("too less bind values");
00729
00730 sql.append(p);
00731
00732 return sql;
00733 }
00734 #endif
00735
00736
00737 MYSQL_RES* SqlStatement::get_meta_res() const
00738 {
00739 #if MYSQLPL_PREP_STMT
00740 if (!_result) {
00741 _result = mysql_stmt_result_metadata(_stmt);
00742
00743 if (!_result)
00744 THROW_MYSQLPL_EXCEPTION("no result set metadata");
00745 }
00746 #endif
00747
00748 return _result;
00749 }
00750
00751 bool SqlStatement::is_select() const
00752 {
00753 #if MYSQLPL_PREP_STMT
00754 if (!_result)
00755 _result = mysql_stmt_result_metadata(_stmt);
00756
00757 return _result!=NULL;
00758 #else
00759 if (!(_state & EXECUTED))
00760 THROW_MYSQLPL_EXCEPTION("statement not yet executed");
00761
00762 return _result!=NULL;
00763 #endif
00764 }
00765
00766
00767 int SqlStatement::get_column_count() const
00768 {
00769 #if MYSQLPL_PREP_STMT
00770 unsigned count = mysql_num_fields(get_meta_res());
00771
00772 if (count == (unsigned)-1)
00773 THROW_MYSQLPL_EXCEPTION(_stmt);
00774
00775 return count;
00776 #else
00777 return mysql_num_fields(_result);
00778 #endif
00779 }
00780
00781 void SqlStatement::get_column_type(int idx, ColumnType& info) const
00782 {
00783 MYSQL_FIELD* field = mysql_fetch_field_direct(get_meta_res(), idx);
00784
00785 if (!field)
00786 #if MYSQLPL_PREP_STMT
00787 THROW_MYSQLPL_EXCEPTION(_stmt);
00788 #else
00789 THROW_MYSQLPL_EXCEPTION(_db);
00790 #endif
00791
00792 info.init(field);
00793 }
00794
00795 void ColumnType::init(const MYSQL_FIELD* field)
00796 {
00797
00798 _table_name = field->table;
00799 _name = field->name;
00800
00801
00802 _data_type = field->type;
00803
00804 _width = field->length;
00805 _decimals = field->decimals;
00806
00807
00808 _flags = field->flags;
00809 }
00810
00811
00812 void SqlStatement::bind_param(int idx, BindPar par)
00813 {
00814 if ((int)_param_bind_vector.size() <= idx)
00815 _param_bind_vector.resize(idx+1);
00816
00817 _param_bind_vector.bind(idx, par);
00818 }
00819
00820
00821 void SqlQuery::bind_param(BindPar par)
00822 {
00823 int idx = _param_bind_vector.size();
00824
00825 _param_bind_vector.resize(idx+1);
00826
00827 _param_bind_vector.bind(idx, par);
00828 }
00829
00830
00831 void BindVector::bind(int idx, const BindPar& par)
00832 {
00833 at(idx) = par;
00834 }
00835
00836
00837 void SqlStatement::print_results(std::ostream& out, bool header)
00838 {
00839 if (!(_state & EXECUTED))
00840 execute();
00841
00842 if (header)
00843 _res->print_columns(out);
00844
00845 int row_cnt = 0;
00846
00847 while(fetch()) {
00848 ++row_cnt;
00849
00850 out << "row " << row_cnt << ":\n";
00851
00852 _res->print_values(out);
00853
00854 out << std::endl;
00855 }
00856 }
00857
00858
00859 SqlRecord::SqlRecord(SqlStatement& stmt)
00860 : _stmt(stmt)
00861 {
00862 int column_cnt = stmt.get_column_count();
00863
00864 _columns.resize(column_cnt);
00865
00866
00867 MYSQL_FIELD* fields = mysql_fetch_fields(stmt.get_meta_res());
00868
00869 for(ColumnVector::iterator it=_columns.begin(); it!=_columns.end(); ++it) {
00870 it->_type.init(fields++);
00871
00872 #if MYSQLPL_PREP_STMT
00873 it->_column = SqlVariant(it->_type._data_type);
00874 #endif
00875 }
00876
00877
00878 }
00879
00880 const ColumnType& SqlRecord::get_column_type(const std::string& col_name) const
00881 {
00882 NameMap::const_iterator found = find_column(col_name);
00883
00884 if (found != _name_map.end()) {
00885 int idx = found->second;
00886 return _columns[idx]._type;
00887 } else {
00888 THROW_MYSQLPL_EXCEPTION("column not found");
00889 return *(ColumnType*)NULL;
00890 }
00891 }
00892
00893 #if MYSQLPL_PREP_STMT
00894 const SqlVariant& SqlRecord::get_column(const std::string& col_name) const
00895 {
00896 NameMap::const_iterator found = find_column(col_name);
00897
00898 if (found != _name_map.end()) {
00899 int idx = found->second;
00900 return _columns[idx]._column;
00901 } else {
00902 THROW_MYSQLPL_EXCEPTION("column not found");
00903 return *(SqlVariant*)NULL;
00904 }
00905 }
00906 #endif
00907
00908 SqlRecord::NameMap::const_iterator SqlRecord::find_column(const std::string& col_name) const
00909 {
00910 std::string upper_name;
00911
00912
00913 if (_name_map.empty()) {
00914 int idx = 0;
00915
00916 for(ColumnVector::const_iterator it=_columns.begin(); it!=_columns.end(); ++it,++idx) {
00917 upper_name = it->_type._name.c_str();
00918 _strupr_s((char*)upper_name.data(), upper_name.length()+1);
00919
00920 _name_map[upper_name] = idx;
00921 }
00922 }
00923
00924 upper_name = col_name;
00925 _strupr_s((char*)upper_name.data(), upper_name.length()+1);
00926
00927 return _name_map.find(upper_name);
00928 }
00929
00930 int SqlRecord::get_column_idx(const std::string& col_name) const
00931 {
00932 NameMap::const_iterator found = find_column(col_name);
00933
00934 if (found != _name_map.end())
00935 return found->second;
00936 else {
00937 THROW_MYSQLPL_EXCEPTION("column not found");
00938 return -1;
00939 }
00940 }
00941
00942
00943 void SqlRecord::print_columns(std::ostream& out)
00944 {
00945 for(ColumnVector::const_iterator it=_columns.begin(); it!=_columns.end(); ++it) {
00946 const ColumnType& type = it->_type;
00947
00948 if (!type._table_name.empty())
00949 out << type._table_name << ".";
00950
00951 out << type._name << ": " << type.get_type_str() << std::endl;
00952 }
00953
00954 out << std::endl;
00955 }
00956
00957 void SqlRecord::print_values(std::ostream& out)
00958 {
00959 #if MYSQLPL_PREP_STMT
00960 for(ColumnVector::const_iterator it=_columns.begin(); it!=_columns.end(); ++it) {
00961 const ColumnType& type = it->_type;
00962
00963
00964
00965
00966 out << type._name << "=" << it->_column.str() << std::endl;
00967 }
00968 #else
00969 for(size_t i=0; i<_columns.size(); ++i) {
00970 const ColumnType& type = _columns[i]._type;
00971
00972
00973
00974
00975 out << type._name << "=" << _stmt->get_string(i) << std::endl;
00976 }
00977 #endif
00978 }
00979
00980
00981 #if MYSQLPL_PREP_STMT
00982
00983 SqlVariant::SqlVariant(enum_field_types data_type)
00984 {
00985 _data_type = data_type;
00986
00987 switch(data_type) {
00988 case MYSQL_TYPE_STRING:
00989 _data_type = MYSQL_TYPE_VAR_STRING;
00990
00991
00992 case MYSQL_TYPE_VAR_STRING:
00993 _string = new SqlString;
00994 break;
00995
00996 case MYSQL_TYPE_TINY:
00997 case MYSQL_TYPE_SHORT:
00998 case MYSQL_TYPE_INT24:
00999 case MYSQL_TYPE_YEAR:
01000 #ifdef FIELD_TYPE_ENUM
01001 case FIELD_TYPE_ENUM:
01002 #endif
01003 _data_type = MYSQL_TYPE_LONG;
01004
01005 case MYSQL_TYPE_LONG:
01006 _int = new SqlInt;
01007 break;
01008
01009 case MYSQL_TYPE_LONGLONG:
01010 _integer = new SqlInteger;
01011 break;
01012
01013 case MYSQL_TYPE_DECIMAL:
01014 #if MYSQL_VERSION_ID>=50000
01015 case MYSQL_TYPE_NEWDECIMAL:
01016 #endif
01017 case MYSQL_TYPE_FLOAT:
01018 _data_type = MYSQL_TYPE_DOUBLE;
01019
01020
01021 case MYSQL_TYPE_DOUBLE:
01022 _number = new SqlNumber;
01023 break;
01024
01025 case MYSQL_TYPE_DATE:
01026 _date = new SqlDate(MYSQL_TIMESTAMP_DATE);
01027 break;
01028
01029 #if MYSQL_VERSION_ID>=50000
01030 case MYSQL_TYPE_NEWDATE:
01031 _date = new SqlDate(MYSQL_TIMESTAMP_DATE);
01032 break;
01033 #endif
01034
01035 case MYSQL_TYPE_DATETIME:
01036 _date = new SqlDate(MYSQL_TIMESTAMP_DATETIME);
01037 break;
01038
01039 case MYSQL_TYPE_TIMESTAMP:
01040 _date = new SqlDate(MYSQL_TIMESTAMP_TIME);
01041 break;
01042
01043 case MYSQL_TYPE_TINY_BLOB:
01044 case MYSQL_TYPE_MEDIUM_BLOB:
01045 case MYSQL_TYPE_LONG_BLOB:
01046 _data_type = MYSQL_TYPE_BLOB;
01047
01048 case MYSQL_TYPE_BLOB:
01049 _blob = new MSqlBlob;
01050 break;
01051
01052 default:
01053 THROW_MYSQLPL_EXCEPTION("unsupported datatype");
01054 }
01055 }
01056
01057 SqlVariant::SqlVariant(const SqlVariant& other)
01058 {
01059 _data_type = (enum_field_types)-1;
01060
01061 assign(other);
01062 }
01063
01064 void SqlVariant::assign(const SqlVariant& other)
01065 {
01066 clear();
01067
01068 _data_type = other._data_type;
01069
01070 switch(other._data_type) {
01071 case -1:
01072 break;
01073
01074 case MYSQL_TYPE_VAR_STRING:
01075 _string = new SqlString(*other._string);
01076 break;
01077
01078 case MYSQL_TYPE_LONG:
01079 _int = new SqlInt(*other._int);
01080 break;
01081
01082 case MYSQL_TYPE_LONGLONG:
01083 _integer = new SqlInteger(*other._integer);
01084 break;
01085
01086 case MYSQL_TYPE_DOUBLE:
01087 _number = new SqlNumber(*other._number);
01088 break;
01089
01090 case MYSQL_TYPE_DATE:
01091 #if MYSQL_VERSION_ID>=50000
01092 case MYSQL_TYPE_NEWDATE:
01093 #endif
01094 case MYSQL_TYPE_DATETIME:
01095 case MYSQL_TYPE_TIME:
01096 case MYSQL_TYPE_TIMESTAMP:
01097 _date = new SqlDate(*other._date);
01098 break;
01099
01100 case MYSQL_TYPE_BLOB:
01101 _blob = new MSqlBlob(*other._blob);
01102 break;
01103
01104 default:
01105 assert(0);
01106 }
01107 }
01108
01109 void SqlVariant::clear()
01110 {
01111 switch(_data_type) {
01112 case -1:
01113 break;
01114
01115 case MYSQL_TYPE_VAR_STRING:
01116 delete _string;
01117 break;
01118
01119 case MYSQL_TYPE_LONG:
01120 delete _int;
01121 break;
01122
01123 case MYSQL_TYPE_LONGLONG:
01124 delete _integer;
01125 break;
01126
01127 case MYSQL_TYPE_DOUBLE:
01128 delete _number;
01129 break;
01130
01131 case MYSQL_TYPE_DATE:
01132 #if MYSQL_VERSION_ID>=50000
01133 case MYSQL_TYPE_NEWDATE:
01134 #endif
01135 case MYSQL_TYPE_DATETIME:
01136 case MYSQL_TYPE_TIME:
01137 case MYSQL_TYPE_TIMESTAMP:
01138 delete _date;
01139 break;
01140
01141 case MYSQL_TYPE_BLOB:
01142 delete _blob;
01143 break;
01144
01145 default:
01146 assert(0);
01147 }
01148
01149 _data_type = (enum_field_types)-1;
01150 }
01151
01152 std::string SqlVariant::str() const
01153 {
01154 switch(_data_type) {
01155 case -1:
01156 return "";
01157
01158 case MYSQL_TYPE_VAR_STRING:
01159 return _string->c_str();
01160
01161
01162 case MYSQL_TYPE_LONG:
01163 return _int->str();
01164
01165 case MYSQL_TYPE_LONGLONG:
01166 return _integer->str();
01167
01168 case MYSQL_TYPE_DOUBLE:
01169 return _number->str();
01170
01171 case MYSQL_TYPE_DATE:
01172 #if MYSQL_VERSION_ID>=50000
01173 case MYSQL_TYPE_NEWDATE:
01174 #endif
01175 case MYSQL_TYPE_DATETIME:
01176 case MYSQL_TYPE_TIME:
01177 case MYSQL_TYPE_TIMESTAMP:
01178 return _date->str();
01179
01180 case MYSQL_TYPE_BLOB:
01181 return _blob->str();
01182 break;
01183
01184 default:
01185 assert(0);
01186 return "";
01187 }
01188 }
01189
01190 SqlNumber SqlVariant::number() const
01191 {
01192 switch(_data_type) {
01193 case -1:
01194 return SqlNumber();
01195
01196 case MYSQL_TYPE_VAR_STRING:
01197 return SqlNumber(*_string);
01198
01199 case MYSQL_TYPE_LONG:
01200 return (int)*_int;
01201
01202 case MYSQL_TYPE_LONGLONG:
01203 return (LONGLONG)*_integer;
01204
01205 case MYSQL_TYPE_DOUBLE:
01206 return *_number;
01207
01208
01209
01210
01211 default:
01212 THROW_MYSQLPL_EXCEPTION("no conversion to number");
01213 }
01214 }
01215
01216 int SqlVariant::get_int() const
01217 {
01218 switch(_data_type) {
01219 case -1:
01220 return 0;
01221
01222 case MYSQL_TYPE_VAR_STRING:
01223 return atoi(*_string);
01224
01225 case MYSQL_TYPE_LONG:
01226 return *_int;
01227
01228 case MYSQL_TYPE_LONGLONG:
01229 return (int)*_integer;
01230
01231 case MYSQL_TYPE_DOUBLE:
01232 return _number->get_int();
01233
01234
01235
01236
01237 default:
01238 THROW_MYSQLPL_EXCEPTION("no conversion to int");
01239 }
01240 }
01241
01242 #ifdef _MSC_VER
01243 LONGLONG SqlVariant::get_int64() const
01244 {
01245 switch(_data_type) {
01246 case -1:
01247 return 0;
01248
01249 case MYSQL_TYPE_VAR_STRING:
01250 return _atoi64(*_string);
01251
01252 case MYSQL_TYPE_LONG:
01253 return *_int;
01254
01255 case MYSQL_TYPE_LONGLONG:
01256 return *_integer;
01257
01258 case MYSQL_TYPE_DOUBLE:
01259 return _number->get_int64();
01260
01261
01262
01263
01264 default:
01265 THROW_MYSQLPL_EXCEPTION("no conversion to int64");
01266 }
01267 }
01268 #endif
01269
01270 double SqlVariant::get_double() const
01271 {
01272 switch(_data_type) {
01273 case -1:
01274 return 0;
01275
01276 case MYSQL_TYPE_VAR_STRING:
01277 return atof(*_string);
01278
01279 case MYSQL_TYPE_LONG:
01280 return *_int;
01281
01282 case MYSQL_TYPE_LONGLONG:
01283 return (double)*_integer;
01284
01285 case MYSQL_TYPE_DOUBLE:
01286 return _number->get_double();
01287
01288
01289
01290
01291 default:
01292 THROW_MYSQLPL_EXCEPTION("no conversion to double");
01293 }
01294 }
01295
01296 SqlDate SqlVariant::date(enum_mysql_timestamp_type type) const
01297 {
01298 switch(_data_type) {
01299 case -1:
01300 return SqlDate();
01301
01302 case MYSQL_TYPE_VAR_STRING:
01303 return SqlDate(*_string, MYSQL_TIMESTAMP_DATE);
01304
01305 case MYSQL_TYPE_DATE:
01306 #if MYSQL_VERSION_ID>=50000
01307 case MYSQL_TYPE_NEWDATE:
01308 #endif
01309 case MYSQL_TYPE_DATETIME:
01310 case MYSQL_TYPE_TIME:
01311 case MYSQL_TYPE_TIMESTAMP:
01312 return SqlDate(*_date, type);
01313
01314 default:
01315 THROW_MYSQLPL_EXCEPTION("no conversion to date type");
01316 }
01317 }
01318
01319 bool SqlVariant::is_null() const
01320 {
01321 switch(_data_type) {
01322 case -1:
01323 return true;
01324
01325 case MYSQL_TYPE_VAR_STRING:
01326 return _string->is_null();
01327
01328 case MYSQL_TYPE_LONG:
01329 return _int->is_null();
01330
01331 case MYSQL_TYPE_LONGLONG:
01332 return _integer->is_null();
01333
01334 case MYSQL_TYPE_DOUBLE:
01335 return _number->is_null();
01336
01337 case MYSQL_TYPE_DATE:
01338 #if MYSQL_VERSION_ID>=50000
01339 case MYSQL_TYPE_NEWDATE:
01340 #endif
01341 case MYSQL_TYPE_DATETIME:
01342 case MYSQL_TYPE_TIME:
01343 case MYSQL_TYPE_TIMESTAMP:
01344 return _date->is_null();
01345
01346 case MYSQL_TYPE_BLOB:
01347 return _blob->is_null();
01348
01349 default:
01350 assert(0);
01351 return true;
01352 }
01353 }
01354
01355 void mysql_bind_param(const BindPar& par, MYSQL_BIND& bind)
01356 {
01357 memset(&bind, 0, sizeof(bind));
01358
01359 bind.buffer = par._ptr;
01360 bind.buffer_type = par._type;
01361 bind.buffer_length = par._len;
01362 bind.is_null = par._pind;
01363 bind.length = par._plen;
01364 }
01365
01366 void SqlStatement::bind_result(ColumnVector& columns)
01367 {
01368 int idx = 0;
01369
01370 for(ColumnVector::iterator it=columns.begin(); it!=columns.end(); ++it)
01371 it->_column.bind_result_column(*this, _result_bind_vector[idx++]);
01372
01373 bind_result(&_result_bind_vector[0]);
01374 }
01375
01376 void SqlStatement::bind_result_column(BindResultPar par, MYSQL_BIND& bind)
01377 {
01378 memset(&bind, 0, sizeof(bind));
01379
01380 bind.buffer = par._ptr;
01381 bind.buffer_type = par._type;
01382 bind.buffer_length = par._len;
01383 bind.is_null = par._pind;
01384 bind.length = par._plen;
01385 }
01386
01387 void SqlVariant::bind_result_column(SqlStatement& stmt, MYSQL_BIND& bind)
01388 {
01389 switch(_data_type) {
01390 case MYSQL_TYPE_VAR_STRING:
01391 stmt.bind_result_column(*_string, bind);
01392 break;
01393
01394 case MYSQL_TYPE_LONG:
01395 stmt.bind_result_column(*_int, bind);
01396 break;
01397
01398 case MYSQL_TYPE_LONGLONG:
01399 stmt.bind_result_column(*_integer, bind);
01400 break;
01401
01402 case MYSQL_TYPE_DOUBLE:
01403 stmt.bind_result_column(*_number, bind);
01404 break;
01405
01406 case MYSQL_TYPE_DATE:
01407 #if MYSQL_VERSION_ID>=50000
01408 case MYSQL_TYPE_NEWDATE:
01409 #endif
01410 case MYSQL_TYPE_DATETIME:
01411 case MYSQL_TYPE_TIME:
01412 case MYSQL_TYPE_TIMESTAMP:
01413 stmt.bind_result_column(*_date, bind);
01414 break;
01415
01416 case MYSQL_TYPE_BLOB:
01417 stmt.bind_result_column(*_blob, bind);
01418 break;
01419
01420 default:
01421 assert(0);
01422 }
01423 }
01424
01425 #endif // MYSQLPL_PREP_STMT
01426
01427
01428 }