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

mysqlpl.cpp

Go to the documentation of this file.
00001 
00002  //
00003  // mysqlpl.cpp
00004  //
00005  // Copyright (c) 2004, 2006, 2007, 2009, 2011, 2012 Martin Fuchs <martin-fuchs@gmx.net>
00006  //
00007 
00008 /*
00009 
00010   All rights reserved.
00011 
00012   Redistribution and use in source and binary forms, with or without
00013   modification, are permitted provided that the following conditions are met:
00014 
00015   * Redistributions of source code must retain the above copyright
00016     notice, this list of conditions and the following disclaimer.
00017   * Redistributions in binary form must reproduce the above copyright
00018     notice, this list of conditions and the following disclaimer in
00019     the documentation and/or other materials provided with the
00020     distribution.
00021 
00022   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00023   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00024   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00025   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00026   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00027   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00028   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00029   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00030   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00031   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032   POSSIBILITY OF SUCH DAMAGE.
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      // disable auto commit mode
00168     my_bool res = mysql_autocommit(_db, 0);
00169     mysqlpl_check_error(_env, res);
00170 #else
00171     SqlStatement(*this).execute("set autocommit=0");    // "set autocommit=0" is not supported before MySQL 4.1
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)   // %lf -> double
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);  // %f -> double
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           //case MYSQL_TIMESTAMP_DATETIME:
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           //case MYSQL_TIMESTAMP_DATETIME:
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 //  _result_bind_vector.clear();
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); // for DML statements
00572 /*
00573     if (res == -1)
00574         res = mysql_stmt_num_rows(_stmt);   // only for SELECT statements
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          // a SELECT statement without ready define variables?
00613         if (is_select()) {  // a statement with result set? (=> SELECT)
00614             _result_bind_vector.resize(get_column_count());
00615 
00616              // define resultset variables
00617             assert(!_res);
00618             _res = new SqlResult(*this);
00619         }
00620     }
00621 
00622     MSqlBindVector param_mbind_vector;
00623 
00624      // bind parameter values
00625     if (!_param_bind_vector.empty()) {
00626          // Umwandlung der Bindparameter von BindPar in MYSQL_BIND-Strukturelemente
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, &param_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) { // is_select()
00652          // define resultset variables
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          // look for quotes and escape characters
00670         else if (*p == '\'') {
00671             while(*++p)
00672                 if (*p=='\'' && *++p!='\'')
00673                     break;
00674         }
00675 
00676     return NULL;
00677 }
00678 
00679  // create sql statement from prepared statement and integrate bind variables
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              // escape quote characters in bound string constants
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      // get column name
00798     _table_name = field->table;
00799     _name = field->name;
00800 
00801      // get column data type
00802     _data_type = field->type;
00803 
00804     _width = field->length;
00805     _decimals = field->decimals;
00806 
00807      // get NULL-ability, binary, ... flags
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 //  unsigned num_fields = mysql_num_fields(stmt.get_meta_res());
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     //_name_map.clear();
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      // initialize column name map
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 //      if (!type._table_name.empty())
00964 //          out << type._table_name << ".";
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 //      if (!type._table_name.empty())
00973 //          out << type._table_name << ".";
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         // fall through
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         // fall through
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         // fall through
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         // fall through
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 //      return *_string;
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     //case MYSQL_TYPE_DATETIME:
01209     //  return ...
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     //case MYSQL_TYPE_DATETIME:
01235     //  return ...
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     //case MYSQL_TYPE_DATETIME:
01262     //  return ...
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     //case MYSQL_TYPE_DATETIME:
01289     //  return ...
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 } // namespace MySqlPL

Generated on Fri Jan 31 2014 00:11:58 for MySQLPL/C++ by  doxygen 1.7.2