├── .gitignore ├── AdhocParameter.cpp ├── AdhocParameter.h ├── AdhocStatement.cpp ├── AdhocStatement.h ├── Binary.cpp ├── Binary.h ├── Database.cpp ├── Database.h ├── DatabaseException.cpp ├── DatabaseException.h ├── IStatement.cpp ├── IStatement.h ├── Julian.cpp ├── Julian.h ├── Makefile ├── Makefile.bug ├── Nullable.h ├── ParamBuffer.cpp ├── ParamBuffer.h ├── README.md ├── Statement.cpp ├── Statement.h ├── TestAdhocParameter.cpp ├── TestAdhocParameter.h ├── TestBinary.cpp ├── TestBinary.h ├── TestDatabase.cpp ├── TestDatabase.h ├── TestDatabaseException.cpp ├── TestDatabaseException.h ├── TestImport.cpp ├── TestImport.h ├── TestJulian.cpp ├── TestJulian.h ├── TestNullable.cpp ├── TestNullable.h ├── UTFail.cpp ├── UTFail.h ├── bug.cpp ├── bugreport.readme ├── mike.jpg ├── pic.jpg ├── program.cpp ├── sakila-db ├── sakila-data.sql ├── sakila-schema.sql └── sakila.mwb ├── test.cpp └── testemb.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | bin/ 3 | cover/ 4 | -------------------------------------------------------------------------------- /AdhocParameter.cpp: -------------------------------------------------------------------------------- 1 | #include "AdhocParameter.h" 2 | #include "DatabaseException.h" 3 | 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | namespace MySQLWrap { 10 | 11 | AdhocParameter::AdhocParameter() { 12 | _isNull = true; 13 | } 14 | 15 | bool AdhocParameter::IsNull() const { 16 | return _isNull; 17 | } 18 | 19 | void AdhocParameter::SetData(const unsigned char data) { 20 | _isNull = false; 21 | _data << std::dec << data; 22 | } 23 | 24 | void AdhocParameter::SetData(const char data) { 25 | _isNull = false; 26 | _data << std::dec << data; 27 | } 28 | 29 | void AdhocParameter::SetData(const unsigned short data) { 30 | _isNull = false; 31 | _data << std::dec << data; 32 | } 33 | 34 | void AdhocParameter::SetData(const short data) { 35 | _isNull = false; 36 | _data << std::dec << data; 37 | } 38 | 39 | void AdhocParameter::SetData(const unsigned int data) { 40 | _isNull = false; 41 | _data << std::dec << data; 42 | } 43 | 44 | void AdhocParameter::SetData(const int data) { 45 | _isNull = false; 46 | _data << std::dec << data; 47 | } 48 | 49 | void AdhocParameter::SetData(const std::string &data) { 50 | _isNull = false; 51 | _data << "'" << data << "'"; 52 | } 53 | 54 | void AdhocParameter::SetData(const Binary &data) { 55 | _isNull = false; 56 | char buff[data.BufferLength() * 2 + 1]; 57 | if (mysql_hex_string(buff, (const char *)data.Buffer(), data.BufferLength()) != data.BufferLength() * 2) { 58 | throw DatabaseException("Error in AdhocParameter::SetData", 0, "----", "mysql_hex_string returned the wrong size string"); 59 | } 60 | _data << "X'" << buff << "'"; 61 | } 62 | 63 | void AdhocParameter::SetData(const float data) { 64 | _isNull = false; 65 | _data << data; 66 | } 67 | 68 | void AdhocParameter::SetData(const double data) { 69 | _isNull = false; 70 | _data << data; 71 | } 72 | 73 | void AdhocParameter::SetData(const Julian &julian) { 74 | _isNull = false; 75 | GregorianBreakdown data = julian.to_gregorian(0); 76 | 77 | if (julian.Type() == TimeType::DateTime) { 78 | _data << "'" << setfill('0') << setw(4) << data.year << "-" << setw(2) << data.month << "-" << setw(2) << data.day << " " 79 | << setw(2) << data.hour << ":" << setw(2) << data.minute << ":" << setw(2) << data.second; 80 | if (data.millisecond > 0) { 81 | _data << "." << setw(6) << data.millisecond * 1000; 82 | } 83 | _data << setfill(' ') << "'"; 84 | } else if (julian.Type() == TimeType::Date) { 85 | _data << "'" << setfill('0') << setw(4) << data.year << "-" << setw(2) << data.month << "-" << setw(2) << data.day << setfill(' ') << "'"; 86 | } else if (julian.Type() == TimeType::Time) { 87 | unsigned long hours = data.day * 24 + data.hour; 88 | _data << "'" << setfill('0') 89 | << setw(2) << hours << ":" << setw(2) << data.minute << ":" << setw(2) << data.second; 90 | if (data.millisecond > 0) { 91 | _data << "." << setw(6) << data.millisecond * 1000; 92 | } 93 | _data << "'" << setfill(' '); 94 | } 95 | } 96 | 97 | std::string AdhocParameter::Get() const { 98 | return (_isNull) ? "NULL" : _data.str(); 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /AdhocParameter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "Binary.h" 7 | #include "Julian.h" 8 | 9 | #include 10 | #include 11 | 12 | namespace MySQLWrap { 13 | 14 | class AdhocParameter { 15 | public: 16 | AdhocParameter(); 17 | void SetData(const unsigned char data); 18 | void SetData(const char data); 19 | void SetData(const unsigned short int data); 20 | void SetData(const short int data); 21 | void SetData(const unsigned int data); 22 | void SetData(const int data); 23 | void SetData(const std::string &data); 24 | void SetData(const Julian &data); 25 | void SetData(const Binary &data); 26 | void SetData(const float data); 27 | void SetData(const double data); 28 | 29 | std::string Get() const; 30 | bool IsNull() const; 31 | private: 32 | bool _isNull; 33 | std::stringstream _data; 34 | }; 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /AdhocStatement.cpp: -------------------------------------------------------------------------------- 1 | #include "AdhocStatement.h" 2 | #include "DatabaseException.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | 13 | namespace MySQLWrap { 14 | 15 | AdhocStatement::AdhocStatement(Database &db, const string &sqlStatement) : _db(db) { 16 | _currentColumn = 0; 17 | _numberResultColumns = 0; 18 | _numberAffectedRows = 0; 19 | _numberResultRows = 0; 20 | _numberParams = 0; 21 | 22 | _resultWasStored = false; 23 | _eof = true; 24 | 25 | StoreSqlStatement(sqlStatement); 26 | Prepare(); 27 | } 28 | 29 | AdhocStatement::AdhocStatement(const AdhocStatement &stmt) : _db(stmt._db) { 30 | _sqlStatement = stmt._sqlStatement; 31 | _currentColumn = 0; 32 | _numberResultColumns = 0; 33 | _numberAffectedRows = 0; 34 | _numberResultRows = 0; 35 | _numberParams = stmt._numberParams; 36 | 37 | _resultWasStored = false; 38 | _eof = true; 39 | 40 | Prepare(); 41 | } 42 | 43 | AdhocStatement::~AdhocStatement() { 44 | if (_result != NULL) { 45 | mysql_free_result(_result); 46 | } 47 | 48 | _result = NULL; 49 | _numberResultColumns = 0; 50 | _numberAffectedRows = 0; 51 | _numberResultRows = 0; 52 | 53 | _resultWasStored = false; 54 | _eof = true; 55 | 56 | ResetParameters(); 57 | } 58 | 59 | void AdhocStatement::StoreSqlStatement(const std::string &sqlStatement) { 60 | wchar_t dest[sqlStatement.length() + 1]; 61 | memset(dest, 0, sizeof(dest)); 62 | size_t charsWritten = mbstowcs(dest, sqlStatement.c_str(), sqlStatement.length()); 63 | _sqlStatement = std::wstring(dest, charsWritten); 64 | } 65 | 66 | void AdhocStatement::ScanForInsertions() { 67 | wchar_t insideQuote = L'\0'; 68 | for (size_t i = 0; i < _sqlStatement.length(); i++) { 69 | wchar_t it = _sqlStatement[i]; 70 | if ((it == L'\'') || (it == L'"')) { 71 | if (insideQuote == it) { 72 | insideQuote = L'\0'; 73 | } else if (insideQuote == L'\0') { 74 | insideQuote = it; 75 | } 76 | } 77 | if (it == L'?') { 78 | if (insideQuote == L'\0') { 79 | _numberParams += 1; 80 | } 81 | } 82 | } 83 | } 84 | 85 | unsigned long AdhocStatement::ParameterCount() { 86 | return _numberParams; 87 | } 88 | 89 | unsigned long AdhocStatement::RemainingParameters() { 90 | return _numberParams - _params.size(); 91 | } 92 | 93 | void AdhocStatement::Prepare() { 94 | if (! _db.IsConnected()) { 95 | throw DatabaseException("AdhocStatement::Prepare", 0, "----", "Database is not connected"); 96 | } 97 | _result = NULL; 98 | ScanForInsertions(); 99 | } 100 | 101 | std::string AdhocStatement::ReplaceInsertions() { 102 | std::stringstream result; 103 | char buff[sizeof(wchar_t)]; 104 | unsigned int bufflen; 105 | wchar_t insideQuote = L'\0'; 106 | unsigned int paramCount = 0; 107 | 108 | for (auto it = _sqlStatement.begin(); it != _sqlStatement.end(); it++) { 109 | if ((*it == L'\'') || (*it == L'"')) { 110 | if (insideQuote == *it) { 111 | insideQuote = L'\0'; 112 | } else if (insideQuote == L'\0') { 113 | insideQuote = *it; 114 | } 115 | bufflen = wctomb(buff, *it); 116 | result.write(buff, bufflen); 117 | } 118 | else if ((*it == L'?') && (insideQuote == L'\0')) { 119 | if (paramCount < _params.size()) 120 | { 121 | std::string s = _params[paramCount]->Get(); 122 | result.write(s.data(), s.length()); 123 | paramCount++; 124 | } else { 125 | throw DatabaseException("Error in AdhocStatement::ReplaceInsertions", 0, "----", "unexpected insertion points"); 126 | } 127 | } else { 128 | bufflen = wctomb(buff, *it); 129 | result.write(buff, bufflen); 130 | } 131 | } 132 | return result.str(); 133 | } 134 | 135 | void AdhocStatement::Execute() { 136 | if (! _db.IsConnected()) { 137 | throw DatabaseException("Error in AdhocStatement::Execute", 0, "----", "Database is not connected"); 138 | } 139 | 140 | if (RemainingParameters() != 0) { 141 | throw DatabaseException("Error in AdhocStatement::Execute", 0, "----", "There are stil some unsatisfied parameters"); 142 | } 143 | 144 | std::string sql = ReplaceInsertions(); 145 | if (mysql_real_query(_db._db, sql.data(), sql.length()) != 0) { 146 | throw DatabaseException(_db._db, "Error in AdhocStatement::Prepare"); 147 | } 148 | 149 | _numberResultColumns = mysql_field_count(_db._db); 150 | _result = mysql_store_result(_db._db); 151 | 152 | if (_result == NULL && _numberResultColumns > 0) { 153 | throw DatabaseException(_db._db, "Error in AdhocStatement::Execute"); 154 | } 155 | 156 | _resultWasStored = true; 157 | 158 | if (_numberResultColumns == 0) { 159 | _eof = true; 160 | _numberAffectedRows = mysql_affected_rows(_db._db); 161 | } else { 162 | _eof = false; 163 | _numberResultRows = mysql_num_rows(_result); 164 | _fields = mysql_fetch_fields(_result); 165 | if (_fields == NULL) { 166 | throw DatabaseException("Error in AdhocStatement::Execute", 0, "----", "couldn't retrieve fields from query"); 167 | } 168 | } 169 | } 170 | 171 | void AdhocStatement::ResetParameters() { 172 | while (! _params.empty()) { 173 | AdhocParameter *buf = _params.back(); 174 | _params.pop_back(); 175 | delete buf; 176 | } 177 | } 178 | 179 | void AdhocStatement::AssignNextParameter(const Nullable ¶m) { 180 | AssignNextParameterTemplate(param); 181 | } 182 | 183 | void AdhocStatement::AssignNextParameter(const Nullable ¶m) { 184 | AssignNextParameterTemplate(param); 185 | } 186 | 187 | void AdhocStatement::AssignNextParameter(const Nullable ¶m) { 188 | AssignNextParameterTemplate(param); 189 | } 190 | 191 | void AdhocStatement::AssignNextParameter(const Nullable ¶m) { 192 | AssignNextParameterTemplate(param); 193 | } 194 | 195 | void AdhocStatement::AssignNextParameter(const Nullable ¶m) { 196 | AssignNextParameterTemplate(param); 197 | } 198 | 199 | void AdhocStatement::AssignNextParameter(const Nullable ¶m) { 200 | AssignNextParameterTemplate(param); 201 | } 202 | 203 | void AdhocStatement::AssignNextParameter(const Nullable ¶m) { 204 | AssignNextParameterTemplate(param); 205 | } 206 | 207 | void AdhocStatement::AssignNextParameter(const Nullable ¶m) { 208 | AssignNextParameterTemplate(param); 209 | } 210 | 211 | void AdhocStatement::AssignNextParameter(const Nullable ¶m) { 212 | AssignNextParameterTemplate(param); 213 | } 214 | 215 | void AdhocStatement::AssignNextParameter(const Nullable ¶m) { 216 | AssignNextParameterTemplate(param); 217 | } 218 | 219 | void AdhocStatement::AssignNextParameter(const Nullable ¶m) { 220 | AssignNextParameterTemplate(param); 221 | } 222 | 223 | 224 | void AdhocStatement::AssignNextParameter(AdhocParameter *buffer) { 225 | if (buffer == NULL) { 226 | throw DatabaseException("Error in AdhocStatement::AssignNextParameter", 0, "----", "Buffer cannot be null"); 227 | } 228 | 229 | if (RemainingParameters() == 0) { 230 | delete buffer; 231 | throw DatabaseException("Error in AdhocStatement::AssignNextParameter", 0, "----", "Have already assigned all possible input parameters"); 232 | } 233 | 234 | _params.push_back(buffer); 235 | } 236 | 237 | bool AdhocStatement::FetchNextRow() { 238 | _currentRow = mysql_fetch_row(_result); 239 | if (_currentRow == NULL) { 240 | _eof = true; 241 | return false; 242 | } 243 | 244 | _currentRowLengths = mysql_fetch_lengths(_result); 245 | if (_currentRowLengths == NULL) { 246 | throw DatabaseException("Error in AdhocStatement::Fetch", 0, "----", "fetch lengths returned NULL"); 247 | } 248 | 249 | return true; 250 | } 251 | 252 | bool AdhocStatement::Eof() { 253 | return _eof; 254 | } 255 | 256 | unsigned long long AdhocStatement::NumberOfAffectedRows() { 257 | return _numberAffectedRows; 258 | } 259 | 260 | unsigned long long AdhocStatement::NumberOfReturnedRows() { 261 | return _numberResultRows; 262 | } 263 | 264 | AdhocStatement::operator bool() { 265 | return ! Eof(); 266 | } 267 | 268 | 269 | Nullable AdhocStatement::GetTinyDataInRow(unsigned int column) { 270 | Nullable result; 271 | Nullable val = GetStringDataInRowInternal(column); 272 | if (val.HasValue()) { 273 | if ((_fields[column].type != MYSQL_TYPE_TINY) || ((_fields[column].flags & UNSIGNED_FLAG) != 0)) { 274 | throw DatabaseException("Error in AdhocStatement.GetTinyDataInRow", 0, "----", "database field is not an signed tiny type"); 275 | } 276 | char ival = (char) 0; 277 | if (sscanf(val->c_str(), "%hhi", &ival) != 1) { 278 | throw DatabaseException("Error in AdhocStatement::GetTinyDataInRow", 0, "----", "sscanf failed to produce a char."); 279 | } 280 | result = ival; 281 | } 282 | return result; 283 | } 284 | 285 | Nullable AdhocStatement::GetUTinyDataInRow(unsigned int column) { 286 | Nullable result; 287 | Nullable val = GetStringDataInRowInternal(column); 288 | if (val.HasValue()) { 289 | if ((_fields[column].type != MYSQL_TYPE_TINY) || ((_fields[column].flags & UNSIGNED_FLAG) == 0)) { 290 | throw DatabaseException("Error in AdhocStatement.GetUTinyDataInRow", 0, "----", "database field is not an unsigned tiny type"); 291 | } 292 | unsigned char ival = (unsigned char) 0; 293 | if (sscanf(val->c_str(), "%hhui", &ival) != 1) { 294 | throw DatabaseException("Error in AdhocStatement::GetUTinyDataInRow", 0, "----", "sscanf failed to produce an unsigned char"); 295 | } 296 | result = ival; 297 | } 298 | return result; 299 | } 300 | 301 | Nullable AdhocStatement::GetShortDataInRow(unsigned int column) { 302 | Nullable result; 303 | Nullable val = GetStringDataInRowInternal(column); 304 | if (val.HasValue()) { 305 | if ((_fields[column].type != MYSQL_TYPE_SHORT) || ((_fields[column].flags & UNSIGNED_FLAG) != 0)) { 306 | throw DatabaseException("Error in AdhocStatement.GetShortDataInRow", 0, "----", "database field is not a signed short type"); 307 | } 308 | short int ival = (short int) 0; 309 | if (sscanf(val->c_str(), "%hi", &ival) != 1) { 310 | throw DatabaseException("Error in AdhocStatement::GetUShortDataInRow", 0, "----", "sscanf failed to produce an unsigned short"); 311 | } 312 | result = ival; 313 | } 314 | return result; 315 | } 316 | 317 | Nullable AdhocStatement::GetUShortDataInRow(unsigned int column) { 318 | Nullable result; 319 | Nullable val = GetStringDataInRowInternal(column); 320 | if (val.HasValue()) { 321 | if ((_fields[column].type != MYSQL_TYPE_YEAR) && ((_fields[column].type != MYSQL_TYPE_SHORT) || ((_fields[column].flags & UNSIGNED_FLAG) == 0))) { 322 | throw DatabaseException("Error in AdhocStatement.GetUShortDataInRow", 0, "----", "database field is not a unsigned short or year type"); 323 | } 324 | unsigned short int ival = (unsigned short int) 0; 325 | if (sscanf(val->c_str(), "%hui", &ival) != 1) { 326 | throw DatabaseException("Error in AdhocStatement::GetUShortDataInRow", 0, "----", "sscanf failed to produce an unsigned short"); 327 | } 328 | result = ival; 329 | } 330 | return result; 331 | } 332 | 333 | Nullable AdhocStatement::GetLongDataInRow(unsigned int column) { 334 | Nullable result; 335 | Nullable val = GetStringDataInRowInternal(column); 336 | if (val.HasValue()) { 337 | if ((_fields[column].type != MYSQL_TYPE_LONG) || ((_fields[column].flags & UNSIGNED_FLAG) != 0)) { 338 | throw DatabaseException("Error in AdhocStatement.GetLongDataInRow", 0, "----", "database field is not a signed int type"); 339 | } 340 | int ival = (short int) 0; 341 | if (sscanf(val->c_str(), "%i", &ival) != 1) { 342 | throw DatabaseException("Error in AdhocStatement::GetLongDataInRow", 0, "----", "sscanf failed to produce an integer"); 343 | } 344 | result = ival; 345 | } 346 | return result; 347 | } 348 | 349 | Nullable AdhocStatement::GetULongDataInRow(unsigned int column) { 350 | Nullable result; 351 | Nullable val = GetStringDataInRowInternal(column); 352 | if (val.HasValue()) { 353 | if ((_fields[column].type != MYSQL_TYPE_LONG) || ((_fields[column].flags & UNSIGNED_FLAG) == 0)) { 354 | throw DatabaseException("Error in AdhocStatement.GetULongDataInRow", 0, "----", "database field is not an unsigned int type"); 355 | } 356 | unsigned int ival = (unsigned int) 0; 357 | if (sscanf(val->c_str(), "%ui", &ival) != 1) { 358 | throw DatabaseException("Error in AdhocStatement::GetLongDataInRow", 0, "----", "sscanf failed to produce an unsigned integer"); 359 | } 360 | result = ival; 361 | } 362 | return result; 363 | } 364 | 365 | Nullable AdhocStatement::GetFloatDataInRow(unsigned int column) { 366 | Nullable result; 367 | Nullable val = GetStringDataInRowInternal(column); 368 | if (val.HasValue()) { 369 | if (_fields[column].type != MYSQL_TYPE_FLOAT) { 370 | throw DatabaseException("Error in AdhocStatement.GetFloatDataInRow", 0, "----", "database field is not a float type"); 371 | } 372 | float ival = (float) 0; 373 | if (sscanf(val->c_str(), "%f", &ival) != 1) { 374 | throw DatabaseException("Error in AdhocStatement::GetLongDataInRow", 0, "----", "sscanf failed to produce a float"); 375 | } 376 | result = ival; 377 | } 378 | return result; 379 | } 380 | 381 | Nullable AdhocStatement::GetDoubleDataInRow(unsigned int column) { 382 | Nullable result; 383 | Nullable val = GetStringDataInRowInternal(column); 384 | if (val.HasValue()) { 385 | if (_fields[column].type != MYSQL_TYPE_DOUBLE) { 386 | throw DatabaseException("Error in AdhocStatement.GetDoubleDataInRow", 0, "----", "database field is not a double type"); 387 | } 388 | double ival = (double) 0; 389 | if (sscanf(val->c_str(), "%lf", &ival) != 1) { 390 | throw DatabaseException("Error in AdhocStatement::GetLongDataInRow", 0, "----", "sscanf failed to produce a double"); 391 | } 392 | result = ival; 393 | } 394 | return result; 395 | } 396 | 397 | Nullable AdhocStatement::GetStringDataInRow(unsigned int column) { 398 | Nullable val = GetStringDataInRowInternal(column); 399 | if (val.HasValue()) { 400 | if ((_fields[column].type != MYSQL_TYPE_VAR_STRING) && 401 | (_fields[column].type != MYSQL_TYPE_STRING) && 402 | (_fields[column].type != MYSQL_TYPE_DECIMAL) && 403 | (_fields[column].type != MYSQL_TYPE_BIT) && 404 | (_fields[column].type != MYSQL_TYPE_VARCHAR)) { 405 | throw DatabaseException("Error in AdhocStatement::GeStringDataInRow", 0, "----", "database field is not a string, decimal, bit, or varchar type"); 406 | } 407 | } 408 | return val; 409 | } 410 | 411 | Nullable AdhocStatement::GetStringDataInRowInternal(unsigned int column) { 412 | Nullable result; 413 | if (column >= _numberResultColumns) { 414 | throw DatabaseException("Error in AdhocStatement::GetStringDataInRowInternal", 0, "----", "column requested outside of range of result set"); 415 | } 416 | 417 | if (_currentRow[column] != NULL) { 418 | result = string(_currentRow[column], _currentRowLengths[column]); 419 | } 420 | 421 | return result; 422 | } 423 | 424 | Nullable AdhocStatement::GetBinaryDataInRow(unsigned int column) { 425 | Nullable result; 426 | if (column >= _numberResultColumns) { 427 | throw DatabaseException("Error in AdhocStatement::GetBinaryDataInRow", 0, "----", "column requested outside of range of result set"); 428 | } 429 | 430 | if ((_fields[column].type != MYSQL_TYPE_BLOB) && 431 | (_fields[column].type != MYSQL_TYPE_TINY_BLOB) && 432 | (_fields[column].type != MYSQL_TYPE_MEDIUM_BLOB) && 433 | (_fields[column].type != MYSQL_TYPE_LONG_BLOB)) { 434 | throw DatabaseException("Error in AdhocStatement::GetBinaryDataInRow", 0, "----", "value is not a blob type"); 435 | } 436 | if (_currentRow[column] != NULL) { 437 | Binary b1; 438 | b1.AssignDataToBuffer((unsigned char *)_currentRow[column], _currentRowLengths[column]); 439 | result = b1; 440 | } 441 | 442 | return result; 443 | } 444 | 445 | Nullable AdhocStatement::GetTimeDataInRow(unsigned int column) { 446 | Nullable result; 447 | Nullable val = GetStringDataInRowInternal(column); 448 | if (val.HasValue()) { 449 | if ((_fields[column].type != MYSQL_TYPE_TIMESTAMP) && 450 | (_fields[column].type != MYSQL_TYPE_DATE) && 451 | (_fields[column].type != MYSQL_TYPE_TIME) && 452 | (_fields[column].type != MYSQL_TYPE_DATETIME)) { 453 | throw DatabaseException("Error in AdhocStatement::GetTimeDataInRow", 0, "----", "value is not a date or time type"); 454 | } 455 | 456 | MYSQL_TIME timeval; 457 | timeval.year = 0; 458 | timeval.month = 0; 459 | timeval.day = 0; 460 | timeval.hour = 0; 461 | timeval.minute = 0; 462 | timeval.second = 0; 463 | timeval.second_part = 0; 464 | 465 | timeval.time_type = MYSQL_TIMESTAMP_NONE; 466 | 467 | if (val.deref().find("-") != std::string::npos) { 468 | int res = sscanf(val->c_str(), "%u-%u-%u %u:%u:%u.%lu", &timeval.year, &timeval.month, &timeval.day, 469 | &timeval.hour, &timeval.minute, &timeval.second, &timeval.second_part); 470 | if (res == 3) { 471 | timeval.time_type = MYSQL_TIMESTAMP_DATE; 472 | } else if (res >= 6) { 473 | timeval.time_type = MYSQL_TIMESTAMP_DATETIME; 474 | } else { 475 | throw DatabaseException("Error in AdhocStatement::GetUShortDataInRow", 0, "----", "sscanf failed to produce a valid time value"); 476 | } 477 | } else if (val.deref().find(":") != std::string::npos) { 478 | int res = sscanf(val->c_str(), "%u:%u:%u.%lu", &timeval.hour, &timeval.minute, &timeval.second, &timeval.second_part); 479 | if (res >= 3) { 480 | timeval.time_type = MYSQL_TIMESTAMP_TIME; 481 | } else { 482 | throw DatabaseException("Error in AdhocStatement::GetUShortDataInRow", 0, "----", "sscanf failed to produce a valid time value"); 483 | } 484 | } 485 | 486 | GregorianBreakdown gb(timeval, 0); 487 | result = Julian(gb); 488 | } 489 | return result; 490 | } 491 | 492 | void AdhocStatement::GetDataInRow(unsigned int column, Nullable &result) { 493 | result = GetStringDataInRowInternal(column); 494 | } 495 | 496 | void AdhocStatement::GetDataInRow(unsigned int column, Nullable &result) { 497 | result = GetTinyDataInRow(column); 498 | } 499 | 500 | void AdhocStatement::GetDataInRow(unsigned int column, Nullable &result) { 501 | result = GetUTinyDataInRow(column); 502 | } 503 | 504 | void AdhocStatement::GetDataInRow(unsigned int column, Nullable &result) { 505 | result = GetShortDataInRow(column); 506 | } 507 | 508 | void AdhocStatement::GetDataInRow(unsigned int column, Nullable &result) { 509 | result = GetUShortDataInRow(column); 510 | } 511 | 512 | void AdhocStatement::GetDataInRow(unsigned int column, Nullable &result) { 513 | result = GetLongDataInRow(column); 514 | } 515 | 516 | void AdhocStatement::GetDataInRow(unsigned int column, Nullable &result) { 517 | result = GetULongDataInRow(column); 518 | } 519 | 520 | void AdhocStatement::GetDataInRow(unsigned int column, Nullable &result) { 521 | result = GetTimeDataInRow(column); 522 | } 523 | 524 | void AdhocStatement::GetDataInRow(unsigned int column, Nullable &result) { 525 | result = GetFloatDataInRow(column); 526 | } 527 | 528 | void AdhocStatement::GetDataInRow(unsigned int column, Nullable &result) { 529 | result = GetDoubleDataInRow(column); 530 | } 531 | 532 | void AdhocStatement::GetDataInRow(unsigned int column, Nullable &result) { 533 | result = GetBinaryDataInRow(column); 534 | } 535 | 536 | unsigned int AdhocStatement::GetNextDataColumn() { 537 | unsigned int result = _currentColumn; 538 | _currentColumn++; 539 | return result; 540 | } 541 | 542 | } 543 | -------------------------------------------------------------------------------- /AdhocStatement.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "Database.h" 6 | #include "Statement.h" 7 | #include "ParamBuffer.h" 8 | #include "Nullable.h" 9 | #include "AdhocParameter.h" 10 | #include "Julian.h" 11 | 12 | #include 13 | #include 14 | 15 | namespace MySQLWrap { 16 | 17 | class AdhocStatement : public IStatement { 18 | public: 19 | AdhocStatement(Database &db, const std::string &sqlStatement); 20 | AdhocStatement(const AdhocStatement ©); 21 | virtual ~AdhocStatement(); 22 | 23 | virtual unsigned long ParameterCount(); 24 | virtual unsigned long RemainingParameters(); 25 | virtual void Execute(); 26 | virtual void ResetParameters(); 27 | virtual bool FetchNextRow(); 28 | virtual bool Eof(); 29 | virtual unsigned long long NumberOfAffectedRows(); 30 | virtual operator bool(); 31 | virtual unsigned long long NumberOfReturnedRows(); 32 | 33 | Nullable GetTinyDataInRow(unsigned int column); 34 | Nullable GetUTinyDataInRow(unsigned int column); 35 | Nullable GetShortDataInRow(unsigned int column); 36 | Nullable GetUShortDataInRow(unsigned int column); 37 | Nullable GetLongDataInRow(unsigned int column); 38 | Nullable GetULongDataInRow(unsigned int column); 39 | Nullable GetTimeDataInRow(unsigned int column); 40 | Nullable GetStringDataInRow(unsigned int column); 41 | Nullable GetBinaryDataInRow(unsigned int column); 42 | Nullable GetFloatDataInRow(unsigned int column); 43 | Nullable GetDoubleDataInRow(unsigned int column); 44 | 45 | virtual void AssignNextParameter(const Nullable &data); 46 | virtual void AssignNextParameter(const Nullable &data); 47 | virtual void AssignNextParameter(const Nullable &data); 48 | virtual void AssignNextParameter(const Nullable &data); 49 | virtual void AssignNextParameter(const Nullable &data); 50 | virtual void AssignNextParameter(const Nullable &data); 51 | virtual void AssignNextParameter(const Nullable &data); 52 | virtual void AssignNextParameter(const Nullable &data); 53 | virtual void AssignNextParameter(const Nullable &data); 54 | virtual void AssignNextParameter(const Nullable &data); 55 | virtual void AssignNextParameter(const Nullable &data); 56 | 57 | virtual void GetDataInRow(unsigned int column, Nullable &data); 58 | virtual void GetDataInRow(unsigned int column, Nullable &data); 59 | virtual void GetDataInRow(unsigned int column, Nullable &data); 60 | virtual void GetDataInRow(unsigned int column, Nullable &data); 61 | virtual void GetDataInRow(unsigned int column, Nullable &data); 62 | virtual void GetDataInRow(unsigned int column, Nullable &data); 63 | virtual void GetDataInRow(unsigned int column, Nullable &data); 64 | virtual void GetDataInRow(unsigned int column, Nullable &data); 65 | virtual void GetDataInRow(unsigned int column, Nullable &data); 66 | virtual void GetDataInRow(unsigned int column, Nullable &data); 67 | virtual void GetDataInRow(unsigned int column, Nullable &data); 68 | 69 | protected: 70 | virtual unsigned int GetNextDataColumn(); 71 | void StoreSqlStatement(const std::string &sqlStatement); 72 | 73 | template 74 | void AssignNextParameterTemplate(const Nullable ¶m) { 75 | AdhocParameter *buffer = new AdhocParameter(); 76 | if (param.HasValue()) { 77 | buffer->SetData(param.const_deref()); 78 | } 79 | AssignNextParameter(buffer); 80 | } 81 | 82 | private: 83 | void Prepare(); 84 | std::string ReplaceInsertions(); 85 | void ScanForInsertions(); 86 | Nullable GetStringDataInRowInternal(unsigned int column); 87 | void AssignNextParameter(AdhocParameter *value); 88 | 89 | unsigned int _numberResultColumns; 90 | unsigned int _currentColumn; 91 | my_ulonglong _numberAffectedRows; 92 | my_ulonglong _numberResultRows; 93 | 94 | Database &_db; 95 | MYSQL_RES *_result; 96 | MYSQL_ROW _currentRow; 97 | MYSQL_FIELD *_fields; 98 | unsigned long* _currentRowLengths; 99 | 100 | std::wstring _sqlStatement; 101 | bool _resultWasStored; 102 | bool _eof; 103 | unsigned int _numberParams; 104 | std::vector _params; 105 | }; 106 | } 107 | -------------------------------------------------------------------------------- /Binary.cpp: -------------------------------------------------------------------------------- 1 | #include "Binary.h" 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | namespace MySQLWrap { 8 | 9 | Binary::Binary() { 10 | _bufferLength = 0; 11 | _bufferSize = 0; 12 | _buffer = NULL; 13 | } 14 | 15 | Binary::Binary(size_t bufferSize) { 16 | _bufferLength = 0; 17 | _bufferSize = bufferSize; 18 | _buffer = NULL; 19 | if (bufferSize > 0) { 20 | _buffer = (unsigned char *)malloc(_bufferSize); 21 | } 22 | } 23 | 24 | Binary::Binary(unsigned char *buffer, size_t bufferLength, size_t bufferSize) { 25 | _bufferSize = bufferSize; 26 | if (bufferLength > bufferSize) { 27 | _bufferSize = bufferLength; 28 | } 29 | _bufferLength = bufferLength; 30 | _buffer = buffer; 31 | } 32 | 33 | Binary::Binary(Binary ©) { 34 | _buffer = NULL; 35 | _bufferLength = 0; 36 | _bufferSize = 0; 37 | SubsumeBuffer(copy); 38 | } 39 | 40 | Binary &Binary::operator=(const Binary &equal) { 41 | if (_bufferSize < equal._bufferSize) { 42 | if (_buffer != NULL) { 43 | free(_buffer); 44 | } 45 | _bufferSize = equal._bufferSize; 46 | _buffer = (unsigned char *)malloc(_bufferSize); 47 | } 48 | _bufferLength = equal._bufferLength; 49 | if (_bufferLength > 0) { 50 | memcpy(_buffer, equal._buffer, _bufferLength); 51 | } 52 | return *this; 53 | } 54 | 55 | void Binary::ClearBuffer() { 56 | if (_buffer != NULL) { 57 | free(_buffer); 58 | _buffer = NULL; 59 | } 60 | _bufferSize = 0; 61 | _bufferLength = 0; 62 | } 63 | 64 | Binary::~Binary() { 65 | ClearBuffer(); 66 | } 67 | 68 | size_t Binary::BufferLength() const { 69 | return _bufferLength; 70 | } 71 | 72 | size_t Binary::BufferSize() const { 73 | return _bufferSize; 74 | } 75 | 76 | unsigned char *Binary::Buffer() const { 77 | return _buffer; 78 | } 79 | 80 | void Binary::ResizeBuffer(size_t newSize) { 81 | if (_bufferLength > newSize) return; 82 | 83 | unsigned char *newBuffer = (unsigned char *)malloc(newSize); 84 | if (_buffer != NULL) 85 | { 86 | if (_bufferLength > 0) { 87 | memcpy(newBuffer, _buffer, _bufferLength); 88 | } 89 | free(_buffer); 90 | } 91 | 92 | _bufferSize = newSize; 93 | _buffer = newBuffer; 94 | } 95 | 96 | void Binary::AssignDataToBuffer(unsigned char *data, size_t dataLength) { 97 | if (_bufferSize < dataLength) { 98 | _bufferLength = 0; 99 | ResizeBuffer(dataLength); 100 | } 101 | 102 | _bufferLength = dataLength; 103 | memcpy(_buffer, data, _bufferLength); 104 | } 105 | 106 | void Binary::SubsumeBuffer(unsigned char *data, size_t dataLength, size_t bufferSize) { 107 | if (_buffer != NULL) { 108 | free(_buffer); 109 | } 110 | _bufferSize = bufferSize; 111 | _bufferLength = dataLength; 112 | _buffer = data; 113 | } 114 | 115 | void Binary::SubsumeBuffer(Binary &binary) { 116 | SubsumeBuffer(binary._buffer, binary._bufferLength, binary._bufferSize); 117 | binary._buffer = NULL; 118 | binary._bufferSize = 0; 119 | binary._bufferLength = 0; 120 | } 121 | 122 | bool operator==(const Binary &left, const Binary &right) { 123 | unsigned char* leftBuffer = left.Buffer(); 124 | unsigned char* rightBuffer = right.Buffer(); 125 | size_t leftLen = left.BufferLength(); 126 | size_t rightLen = right.BufferLength(); 127 | 128 | if (leftLen != rightLen) return false; 129 | 130 | for (size_t i = 0; i < leftLen; i++) { 131 | if (leftBuffer[i] != rightBuffer[i]) return false; 132 | } 133 | return true; 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /Binary.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace MySQLWrap { 6 | class Binary { 7 | public: 8 | Binary(); 9 | Binary(size_t bufferSize); 10 | Binary(unsigned char *buffer, size_t bufferLength, size_t bufferSize); 11 | Binary(Binary ©); 12 | virtual ~Binary(); 13 | Binary &operator=(const Binary &equal); 14 | 15 | void ResizeBuffer(size_t newSize); 16 | void AssignDataToBuffer(unsigned char *data, size_t dataLength); 17 | void SubsumeBuffer(unsigned char *data, size_t dataLength, size_t bufferLength); 18 | void SubsumeBuffer(Binary &binary); 19 | void ClearBuffer(); 20 | 21 | unsigned char *Buffer() const; 22 | size_t BufferLength() const; 23 | size_t BufferSize() const; 24 | private: 25 | unsigned char *_buffer; 26 | size_t _bufferLength; 27 | size_t _bufferSize; 28 | }; 29 | 30 | bool operator==(const Binary &left, const Binary &right); 31 | } 32 | -------------------------------------------------------------------------------- /Database.cpp: -------------------------------------------------------------------------------- 1 | #include "Database.h" 2 | #include "DatabaseException.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | 11 | namespace MySQLWrap { 12 | 13 | Database::Database() { 14 | _host = ""; 15 | _user = ""; 16 | _password = ""; 17 | _database = ""; 18 | _port = 0; 19 | _unixSocket = NULL; 20 | _clientFlag = 0; 21 | _initialCommand = ""; 22 | _isConnected = false; 23 | _autocommit = true; 24 | 25 | if ((_db = mysql_init(NULL)) == NULL) { 26 | throw DatabaseException("Error tryng to initialize MYSQL database", 0, "-----", "insufficient memory"); 27 | } 28 | 29 | if (_embedded) { 30 | mysql_options(_db, MYSQL_READ_DEFAULT_GROUP, "embedded"); 31 | mysql_options(_db, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL); 32 | } 33 | } 34 | 35 | Database::Database(const std::string &host, const std::string &user, const std::string &password, const std::string &database, unsigned int port, const char *unixSocket, unsigned long clientFlag) { 36 | _host = host; 37 | _user = user; 38 | _password = password; 39 | _database = database; 40 | _port = port; 41 | _unixSocket = unixSocket; 42 | _clientFlag = clientFlag; 43 | _initialCommand = ""; 44 | _isConnected = false; 45 | _autocommit = true; 46 | 47 | if ((_db = mysql_init(NULL)) == NULL) { 48 | throw DatabaseException("Error tryng to initialize MYSQL database", 0, "-----", "insufficient memory"); 49 | } 50 | } 51 | 52 | Database::Database(const Database &db) { 53 | _host = db._host; 54 | _user = db._user; 55 | _password = db._password; 56 | _database = db._database; 57 | _port = db._port; 58 | _unixSocket = db._unixSocket; 59 | _clientFlag = db._clientFlag; 60 | _initialCommand = db._initialCommand; 61 | _autocommit = db._autocommit; 62 | 63 | if ((_db = mysql_init(NULL)) == NULL) { 64 | throw DatabaseException("Error trying to initialize MYSQL database", 0, "----", "insufficient memory"); 65 | } 66 | 67 | if (db._isConnected) { 68 | try { 69 | Connect(); 70 | } catch (DatabaseException &) { 71 | mysql_close(_db); 72 | throw; 73 | } 74 | } 75 | 76 | if (! _autocommit) { 77 | try { 78 | Autocommit(_autocommit); 79 | } catch (DatabaseException &) { 80 | mysql_close(_db); 81 | throw; 82 | } 83 | } 84 | } 85 | 86 | Database::~Database() { 87 | mysql_close(_db); 88 | } 89 | 90 | void Database::Connect() { 91 | if (_host == "") { 92 | if (mysql_real_connect(_db, NULL, NULL, NULL, _database.c_str(), 0, NULL, 0) == NULL) { 93 | throw DatabaseException(_db, "Error in Database::Connect"); 94 | } 95 | } else { 96 | if (mysql_real_connect(_db, _host.c_str(), _user.c_str(), _password.c_str(), _database.c_str(), 97 | _port, _unixSocket, _clientFlag) == NULL) { 98 | throw DatabaseException(_db, "Error in Database::Connect"); 99 | } 100 | } 101 | _isConnected = true; 102 | 103 | if (mysql_set_character_set(_db, "utf8") != 0) 104 | { 105 | throw DatabaseException(_db, "Error in DatabaseConnect"); 106 | } 107 | } 108 | 109 | void Database::SetInitialCommand(const std::string &initialCommand) { 110 | _initialCommand = initialCommand; 111 | } 112 | 113 | bool Database::Autocommit() const { 114 | return _autocommit; 115 | } 116 | 117 | void Database::Autocommit(bool setvalue) { 118 | int ac = (setvalue) ? 1 : 0; 119 | 120 | if (mysql_autocommit(_db, ac) != 0) { 121 | throw DatabaseException(_db, "Error in Database::Autocommit"); 122 | } 123 | } 124 | 125 | void Database::StartTransaction() { 126 | if (mysql_query(_db, "START TRANSACTION") != 0) { 127 | throw DatabaseException(_db, "Error in Database::StartTransaction"); 128 | } 129 | } 130 | 131 | void Database::Commit() { 132 | if (mysql_query(_db, "COMMIT") != 0) { 133 | throw DatabaseException(_db, "Error in Database::Commit"); 134 | } 135 | } 136 | 137 | void Database::Rollback() { 138 | if (mysql_query(_db, "ROLLBACK") != 0) { 139 | throw DatabaseException(_db, "Error in Database::Rollback"); 140 | } 141 | } 142 | 143 | bool Database::IsConnected() { 144 | return _isConnected; 145 | } 146 | 147 | void Database::UseDatabase(const string &dbName) { 148 | if (mysql_select_db(_db, dbName.c_str()) != 0) { 149 | throw DatabaseException(_db, "Error in Database::UseDatabase"); 150 | } 151 | } 152 | 153 | 154 | int Database::Execute(const string &statement) { 155 | if (mysql_query(_db, statement.c_str()) != 0) { 156 | throw DatabaseException(_db, "Error in Database::Execute - query"); 157 | } 158 | 159 | MYSQL_RES *res = mysql_store_result(_db); 160 | if (res != NULL) { 161 | mysql_free_result(res); 162 | throw DatabaseException("Error in Database::Execute", 0, "----", "statements passed to Execute should never return results."); 163 | } 164 | 165 | return mysql_affected_rows(_db); 166 | } 167 | 168 | void Database::LibraryInitialize(bool embedded) { 169 | _embedded = embedded; 170 | if (! embedded) { 171 | if (mysql_library_init(0, NULL, NULL) != 0) { 172 | throw DatabaseException("Error in Database::Initialize", 0, "----", "Failure to initialize the database library"); 173 | } 174 | } else { 175 | static const char *server_args[] = { "this_program", "--basedir=/usr/local/mysql", "--datadir=/Users/ravidesai/mysql/data", 176 | "--plugin-dir=/Users/ravidesai/mysql/plugins", "--log-error=/Users/ravidesai/mysql/tmp/test.err", 177 | "--pid-file=/Users/ravidesai/mysql/tmp/test.pid", 178 | "--key_buffer_size=32M", "--log-bin=/Users/ravidesai/mysql/log/logbin" 179 | "--log-bin-trust-function-creators=1" 180 | "--log-bin-trust-routine-creators=1" 181 | }; 182 | static const char *server_groups[] = { "embedded", "server", "this_program_SERVER", (char *) NULL }; 183 | 184 | if (mysql_library_init(sizeof(server_args) / sizeof(char *), (char**) server_args, (char **)server_groups) != 0) { 185 | throw DatabaseException("Error in Database::Initialize", 0, "----", "Failure to initialize the database library"); 186 | } 187 | } 188 | 189 | cout << setlocale(LC_ALL, "") << endl; 190 | } 191 | 192 | void Database::ThreadInitialize() { 193 | if (mysql_thread_init() != 0) { 194 | throw DatabaseException("Error in Database::ThreadInitialize", 0, "----", "Failure to initialize database for this thread"); 195 | } 196 | } 197 | 198 | void Database::LibraryFinalize() { 199 | mysql_library_end(); 200 | } 201 | 202 | void Database::ThreadFinalize() { 203 | mysql_thread_end(); 204 | } 205 | 206 | LibraryInitializer::LibraryInitializer(bool embedded) { 207 | Database::LibraryInitialize(embedded); 208 | } 209 | 210 | LibraryInitializer::~LibraryInitializer() { 211 | Database::LibraryFinalize(); 212 | } 213 | 214 | ThreadInitializer::ThreadInitializer() { 215 | Database::ThreadInitialize(); 216 | } 217 | 218 | ThreadInitializer::~ThreadInitializer() { 219 | Database::ThreadFinalize(); 220 | } 221 | 222 | bool Database::_embedded; 223 | 224 | static inline std::string <rim(std::string &s) { 225 | s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); 226 | return s; 227 | } 228 | 229 | // trim from end 230 | static inline std::string &rtrim(std::string &s) { 231 | s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); 232 | return s; 233 | } 234 | 235 | // trim from both ends 236 | static inline std::string &trim(std::string &s) { 237 | return ltrim(rtrim(s)); 238 | } 239 | 240 | void Database::Source(const string &fileName) { 241 | struct stat filestat; 242 | if (stat(fileName.c_str(), &filestat) != 0) { 243 | throw DatabaseException("Error in Database::Source", 0, "----", ("could not stat file " + fileName).c_str()); 244 | } 245 | 246 | ifstream commands(fileName.c_str(), ios::in); 247 | if (!commands.is_open()) { 248 | throw DatabaseException("Error in Database::Source", 0, "----", ("could not open file: " + fileName).c_str()); 249 | } 250 | 251 | commands.exceptions(std::ios::badbit); 252 | 253 | try { 254 | std::string cmd; 255 | std::string collector; 256 | std::string delimiter = ";"; 257 | size_t delimpos; 258 | bool insideProcOrFunc = false; 259 | while (! commands.eof()) { 260 | getline(commands, cmd); 261 | cmd = trim(cmd); 262 | 263 | size_t dashDashPos = cmd.find("--"); 264 | if (dashDashPos != std::string::npos && dashDashPos == 0) { 265 | // comment node - ignore 266 | continue; 267 | } 268 | 269 | delimpos = cmd.find("USE DATABASE "); 270 | if (delimpos != std::string::npos && delimpos == 0) { 271 | cmd.erase(0, 13); 272 | cmd = trim(cmd); 273 | UseDatabase(cmd); 274 | continue; 275 | } 276 | 277 | delimpos = cmd.find("DELIMITER "); 278 | if (delimpos != std::string::npos && delimpos == 0) { 279 | delimiter = cmd.erase(0, 10); 280 | delimiter = trim(delimiter); 281 | continue; 282 | } 283 | 284 | delimpos = cmd.find("CREATE PROC"); 285 | if (delimpos != std::string::npos && delimpos == 0) { 286 | insideProcOrFunc = true; 287 | } 288 | delimpos = cmd.find("CREATE FUNC"); 289 | if (delimpos != std::string::npos && delimpos == 0) { 290 | insideProcOrFunc = true; 291 | } 292 | 293 | if (insideProcOrFunc) { 294 | size_t poundPos = cmd.find("#"); 295 | if (poundPos != std::string::npos) { 296 | // remove pound comment 297 | cmd = cmd.erase(poundPos, cmd.length() - poundPos + 1); 298 | } 299 | } 300 | 301 | collector.append(" "); 302 | collector.append(cmd); 303 | 304 | delimpos = collector.rfind(delimiter.c_str()); 305 | if (delimpos != std::string::npos && delimpos + delimiter.length() >= collector.length()) { 306 | // execute the line 307 | collector.replace(delimpos, delimiter.length(), ""); 308 | 309 | if (insideProcOrFunc) { 310 | size_t commentStart = collector.find("/*"); 311 | while (commentStart != std::string::npos) { 312 | size_t commentEnd = collector.find("*/"); 313 | if (commentEnd == std::string::npos || commentEnd <= commentStart) { 314 | throw DatabaseException("Error in Database::Source", 0, "----", "unmatched comment strings"); 315 | } 316 | collector = collector.replace(commentStart, commentEnd - commentStart + 2, ""); 317 | commentStart = collector.find("/*"); 318 | } 319 | } 320 | 321 | Execute(collector); 322 | collector = ""; 323 | insideProcOrFunc = false; 324 | } 325 | } 326 | } catch (ifstream::failure &ex) { 327 | throw DatabaseException("Error while reading file in Database::Source", 0, "----", ex.what()); 328 | } 329 | 330 | commands.close(); 331 | } 332 | } 333 | -------------------------------------------------------------------------------- /Database.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | namespace MySQLWrap { 10 | class Database { 11 | friend class Statement; 12 | friend class AdhocStatement; 13 | public: 14 | Database(const std::string &host, const std::string& user, const std::string &password, const std::string &database, unsigned int port, const char *unixSocket, unsigned long clientFlags); 15 | Database(); 16 | Database(const Database &db); 17 | virtual ~Database(); 18 | 19 | static void LibraryInitialize(bool embedded); 20 | static void ThreadInitialize(); 21 | 22 | static void LibraryFinalize(); 23 | static void ThreadFinalize(); 24 | 25 | void SetInitialCommand(const std::string &initialCommand); 26 | bool Autocommit() const; 27 | void Autocommit(bool value); 28 | void Connect(); 29 | 30 | void StartTransaction(); 31 | void Commit(); 32 | void Rollback(); 33 | void UseDatabase(const string &dbName); 34 | int Execute(const string &statement); 35 | void Source(const string &fileName); 36 | 37 | bool IsConnected(); 38 | 39 | private: 40 | MYSQL *_db; 41 | 42 | std::string _host; 43 | std::string _user; 44 | std::string _password; 45 | std::string _database; 46 | std::string _initialCommand; 47 | unsigned int _port; 48 | const char *_unixSocket; 49 | unsigned long _clientFlag; 50 | 51 | bool _isConnected; 52 | bool _autocommit; 53 | 54 | static bool _embedded; 55 | }; 56 | 57 | class LibraryInitializer { 58 | public: 59 | LibraryInitializer(bool embedded); 60 | ~LibraryInitializer(); 61 | }; 62 | 63 | class ThreadInitializer { 64 | public: 65 | ThreadInitializer(); 66 | ~ThreadInitializer(); 67 | }; 68 | } 69 | -------------------------------------------------------------------------------- /DatabaseException.cpp: -------------------------------------------------------------------------------- 1 | #include "DatabaseException.h" 2 | 3 | namespace MySQLWrap { 4 | 5 | DatabaseException::DatabaseException(const std::string &initialMessage, const int errno, const char *sqlState, const char *errorMessage) { 6 | _initialMessage = initialMessage; 7 | _errno = errno; 8 | _sqlState = sqlState; 9 | _errorMessage = errorMessage; 10 | } 11 | 12 | DatabaseException::DatabaseException(MYSQL *db, const std::string &initialMessage) { 13 | _errno = mysql_errno(db); 14 | _errorMessage = mysql_error(db); 15 | _initialMessage = initialMessage; 16 | _sqlState = mysql_sqlstate(db); 17 | } 18 | 19 | DatabaseException::DatabaseException(MYSQL_STMT *stmt, const std::string &initialMessage) { 20 | _errno = mysql_stmt_errno(stmt); 21 | _errorMessage = mysql_stmt_error(stmt); 22 | _sqlState = mysql_stmt_sqlstate(stmt); 23 | _initialMessage = initialMessage; 24 | } 25 | 26 | std::ostream &operator<<(std::ostream &out, const DatabaseException &exp) { 27 | out << exp._initialMessage << " ERROR " << exp._errno << "(" << exp._sqlState << ") " << exp._errorMessage; 28 | return out; 29 | } 30 | 31 | } 32 | 33 | -------------------------------------------------------------------------------- /DatabaseException.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace MySQLWrap { 7 | class DatabaseException { 8 | public: 9 | DatabaseException(const std::string &initialMessage, const int errno, const char *sqlState, const char *errorMessage); 10 | DatabaseException(MYSQL *db, const std::string &initialMessage); 11 | DatabaseException(MYSQL_STMT *stmt, const std::string &initialMessage); 12 | 13 | friend std::ostream &operator<<(std::ostream &out, const DatabaseException &exp); 14 | 15 | private: 16 | int _errno; 17 | std::string _sqlState; 18 | std::string _initialMessage; 19 | std::string _errorMessage; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /IStatement.cpp: -------------------------------------------------------------------------------- 1 | #include "IStatement.h" 2 | 3 | namespace MySQLWrap { 4 | 5 | ExecuteSentinel::ExecuteSentinel() {} 6 | 7 | IStatement &operator<<(IStatement &stmt, const ExecuteSentinel&) { 8 | stmt.Execute(); 9 | return stmt; 10 | } 11 | 12 | FetchSentinel::FetchSentinel() {} 13 | 14 | IStatement &operator<<(IStatement &stmt, const FetchSentinel&) { 15 | stmt.FetchNextRow(); 16 | return stmt; 17 | } 18 | 19 | ResetSentinel::ResetSentinel() {} 20 | 21 | IStatement &operator<<(IStatement &stmt, const ResetSentinel&) { 22 | stmt.ResetParameters(); 23 | return stmt; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /IStatement.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Database.h" 5 | #include "ParamBuffer.h" 6 | #include "Nullable.h" 7 | #include 8 | 9 | namespace MySQLWrap { 10 | 11 | class ExecuteSentinel { 12 | public: 13 | ExecuteSentinel(); 14 | }; 15 | const ExecuteSentinel execute; 16 | 17 | class FetchSentinel { 18 | public: 19 | FetchSentinel(); 20 | }; 21 | const FetchSentinel fetch; 22 | 23 | class ResetSentinel { 24 | public: 25 | ResetSentinel(); 26 | }; 27 | const ResetSentinel reset; 28 | 29 | class IStatement { 30 | public: 31 | IStatement() {} 32 | virtual ~IStatement() {}; 33 | 34 | virtual unsigned long ParameterCount() = 0; 35 | virtual unsigned long RemainingParameters() = 0; 36 | virtual void ResetParameters() = 0; 37 | virtual void Execute() = 0; 38 | virtual bool FetchNextRow() = 0; 39 | virtual bool Eof() = 0; 40 | virtual unsigned long long NumberOfAffectedRows() = 0; 41 | virtual operator bool() = 0; 42 | virtual unsigned long long NumberOfReturnedRows() = 0; 43 | 44 | // virtual Nullable GetTinyDataInRow(unsigned int column) = 0; 45 | // virtual Nullable GetUTinyDataInRow(unsigned int column) = 0; 46 | // virtual Nullable GetShortDataInRow(unsigned int column) = 0; 47 | // virtual Nullable GetUShortDataInRow(unsigned int column) = 0; 48 | // virtual Nullable GetLongDataInRow(unsigned int column) = 0; 49 | // virtual Nullable GetULongDataInRow(unsigned int column) = 0; 50 | // virtual Nullable GetTimeDataInRow(unsigned int column) = 0; 51 | // virtual Nullable GetStringDataInRow(unsigned int column) = 0; 52 | // virtual Nullable GetBinaryDataInRow(unsigned int column) = 0; 53 | // virtual Nullable GetFloatDataInRow(unsigned int column) = 0; 54 | // virtual Nullable GetDoubleDataInRow(unsigned int column) = 0; 55 | 56 | virtual void AssignNextParameter(const Nullable &data) = 0; 57 | virtual void AssignNextParameter(const Nullable &data) = 0; 58 | virtual void AssignNextParameter(const Nullable &data) = 0; 59 | virtual void AssignNextParameter(const Nullable &data) = 0; 60 | virtual void AssignNextParameter(const Nullable &data) = 0; 61 | virtual void AssignNextParameter(const Nullable &data) = 0; 62 | virtual void AssignNextParameter(const Nullable &data) = 0; 63 | virtual void AssignNextParameter(const Nullable &data) = 0; 64 | virtual void AssignNextParameter(const Nullable &data) = 0; 65 | virtual void AssignNextParameter(const Nullable &data) = 0; 66 | virtual void AssignNextParameter(const Nullable &data) = 0; 67 | 68 | virtual void GetDataInRow(unsigned int column, Nullable &data) = 0; 69 | virtual void GetDataInRow(unsigned int column, Nullable &data) = 0; 70 | virtual void GetDataInRow(unsigned int column, Nullable &data) = 0; 71 | virtual void GetDataInRow(unsigned int column, Nullable &data) = 0; 72 | virtual void GetDataInRow(unsigned int column, Nullable &data) = 0; 73 | virtual void GetDataInRow(unsigned int column, Nullable &data) = 0; 74 | virtual void GetDataInRow(unsigned int column, Nullable &data) = 0; 75 | virtual void GetDataInRow(unsigned int column, Nullable &data) = 0; 76 | virtual void GetDataInRow(unsigned int column, Nullable &data) = 0; 77 | virtual void GetDataInRow(unsigned int column, Nullable &data) = 0; 78 | virtual void GetDataInRow(unsigned int column, Nullable &data) = 0; 79 | 80 | template IStatement &operator>>(Nullable &data) { 81 | GetDataInRow(GetNextDataColumn(), data); 82 | return *this; 83 | } 84 | 85 | protected: 86 | virtual unsigned int GetNextDataColumn() = 0; 87 | }; 88 | 89 | IStatement &operator<<(IStatement &stmt, const ExecuteSentinel&); 90 | IStatement &operator<<(IStatement &stmt, const FetchSentinel&); 91 | IStatement &operator<<(IStatement &stmt, const ResetSentinel&); 92 | 93 | template IStatement &operator<<(IStatement &stmt, const Nullable ¶m) { 94 | stmt.AssignNextParameter(param); 95 | return stmt; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /Julian.cpp: -------------------------------------------------------------------------------- 1 | #include "Julian.h" 2 | #include 3 | 4 | using namespace std; 5 | 6 | namespace MySQLWrap { 7 | 8 | GregorianBreakdown::GregorianBreakdown() { 9 | year = month = day = hour = minute = second = millisecond = 0; 10 | minutes_west_utc = 0; 11 | time_type = TimeType::DateTime; 12 | } 13 | 14 | GregorianBreakdown::GregorianBreakdown(int inyear, unsigned int inmonth, unsigned int inday, 15 | unsigned int inhour, unsigned int inminute, unsigned int insecond, unsigned int inms, int inminutes_west) { 16 | year = inyear; 17 | month = inmonth; 18 | day = inday; 19 | hour = inhour; 20 | minute = inminute; 21 | second = insecond; 22 | millisecond = inms; 23 | time_type = TimeType::DateTime; 24 | minutes_west_utc = inminutes_west; 25 | } 26 | 27 | GregorianBreakdown::GregorianBreakdown(int inyear, unsigned int inmonth, unsigned int inday) { 28 | year = inyear; 29 | month = inmonth; 30 | day = inday; 31 | time_type = TimeType::Date; 32 | minutes_west_utc = 0; 33 | } 34 | 35 | GregorianBreakdown::GregorianBreakdown(unsigned int inday, unsigned int inhour, unsigned int inminute, unsigned int insecond, unsigned int inms) { 36 | minutes_west_utc = 0; 37 | day = inday; 38 | hour = inhour; 39 | minute = inminute; 40 | second = insecond; 41 | millisecond = inms; 42 | time_type = TimeType::Time; 43 | } 44 | 45 | GregorianBreakdown::GregorianBreakdown(const MYSQL_TIME &time, int minutes_west) { 46 | year = (int) time.year; 47 | if (year < 0) { 48 | throw JulianException("Error converting from MYSQL_TIME - year is out of range"); 49 | } 50 | month = time.month; 51 | day = time.day; 52 | hour = time.hour; 53 | minute = time.minute; 54 | second = time.second; 55 | millisecond = (int)(time.second_part / 1000); 56 | minutes_west_utc = minutes_west; 57 | 58 | if (time.time_type == MYSQL_TIMESTAMP_DATE) { 59 | time_type = TimeType::Date; 60 | } else if (time.time_type == MYSQL_TIMESTAMP_TIME) { 61 | time_type = TimeType::Time; 62 | } else { 63 | time_type = TimeType::DateTime; 64 | } 65 | } 66 | 67 | MYSQL_TIME GregorianBreakdown::to_mysql_time() const { 68 | if (year < 0) { 69 | cout << year << "-" << month << "-" << day << " " << hour << ":" << minute << ":" << second << "." << millisecond << endl; 70 | throw JulianException("Cannot convert to MYSQL_TIME, since year is BCE"); 71 | } 72 | 73 | MYSQL_TIME result; 74 | 75 | result.year = (unsigned int) year; 76 | result.month = month; 77 | result.day = day; 78 | result.hour = hour; 79 | result.minute = minute; 80 | result.second = second; 81 | result.second_part = millisecond * 1000; 82 | 83 | if (time_type == TimeType::Date) { 84 | result.time_type = MYSQL_TIMESTAMP_DATE; 85 | } else if (time_type == TimeType::Time) { 86 | result.time_type = MYSQL_TIMESTAMP_TIME; 87 | } else { 88 | result.time_type = MYSQL_TIMESTAMP_DATETIME; 89 | } 90 | 91 | return result; 92 | } 93 | 94 | std::ostream &operator<<(std::ostream &out, const GregorianBreakdown &gb) { 95 | if (gb.time_type != TimeType::Time) { 96 | int year = gb.year; 97 | string era = ""; 98 | if (year <= 0) { 99 | era = "BCE "; 100 | year = (-year) + 1; 101 | } 102 | 103 | out << setfill(' ') << era << year << "-" << setfill('0') << setw(2) << gb.month << "-" << setw(2) << gb.day; 104 | 105 | if (gb.time_type != TimeType::Date) { 106 | out << " "; 107 | } 108 | } 109 | 110 | if (gb.time_type != TimeType::Date) { 111 | out << setfill('0'); 112 | if (gb.time_type == TimeType::Time && gb.day > 0) { 113 | out << gb.day << " "; 114 | } 115 | out << setw(2) << gb.hour << ":" << setw(2) << gb.minute << ":" << setw(2) << gb.second; 116 | if (gb.millisecond > 0) { 117 | out << "." << setw(3) << gb.millisecond; 118 | } 119 | } 120 | 121 | if (gb.time_type != TimeType::Date) { 122 | char plusOrMinus = (gb.minutes_west_utc > 0) ? '-' : '+'; 123 | out << " UTC " << plusOrMinus << setprecision(2) << ((float) abs(gb.minutes_west_utc) / 60); 124 | } 125 | 126 | out << setfill(' '); 127 | return out; 128 | } 129 | 130 | Julian::Julian() { 131 | _julian = 0; 132 | _time_type = TimeType::Time; 133 | } 134 | 135 | Julian::Julian(double invalue, TimeType intype) { 136 | _julian = invalue; 137 | _time_type = intype; 138 | if (invalue < 0) { 139 | throw JulianException("Date would result in an invalid (negative) Julian date."); 140 | } 141 | } 142 | 143 | Julian::Julian(const GregorianBreakdown &gb) { 144 | _julian = calculate_utc(gb.year, gb.month, gb.day, gb.hour, gb.minute, gb.second, gb.millisecond); 145 | _julian += ((double) gb.minutes_west_utc) / 1440; 146 | if (_julian < 0) { 147 | throw JulianException("Date would result in an invalid (negative) Julian date."); 148 | } 149 | _time_type = gb.time_type; 150 | } 151 | 152 | Julian::Julian(int year, unsigned int month, unsigned int day, unsigned int hour, unsigned int minute, unsigned int second, unsigned int ms) { 153 | _julian = calculate_utc(year, month, day, hour, minute, second, ms); 154 | if (_julian < 0) { 155 | throw JulianException("Date would result in an invalid (negative) Julian date."); 156 | } 157 | _time_type = TimeType::DateTime; 158 | } 159 | 160 | Julian::Julian(int year, unsigned int month, unsigned int day) { 161 | _julian = calculate_utc(year, month, day, 0, 0, 0, 0); 162 | if (_julian < 0) { 163 | throw JulianException("Date would result in an invalid (negative) Julian date."); 164 | } 165 | _time_type = TimeType::Date; 166 | } 167 | 168 | Julian::Julian(unsigned int day, unsigned int hour, unsigned int minute, unsigned int second, unsigned int ms) { 169 | _julian = day + calculate_time(hour, minute, second, ms); 170 | _time_type = TimeType::Time; 171 | } 172 | 173 | TimeType Julian::Type() const { 174 | return _time_type; 175 | } 176 | 177 | double Julian::Value() const { 178 | return _julian; 179 | } 180 | 181 | double Julian::calculate_utc(int year, unsigned int month, unsigned int day, unsigned int hour, unsigned int minute, unsigned int second, unsigned int ms) { 182 | return calculate_jdn(year, month, day) + calculate_time(hour, minute, second, ms); 183 | } 184 | 185 | double Julian::calculate_jdn(int year, unsigned int month, unsigned int day) { 186 | int a = (14 - month) / 12; 187 | int y = year + 4800 - a; 188 | int m = month + (12 * a) - 3; 189 | 190 | int jdn = day + (int)(((153 * m) + 2) / 5) + (365*y) + 191 | (int)(y/4) - (int)(y / 100) + (int)(y/400) - 32045; 192 | 193 | return ((double) jdn) - 0.5; 194 | } 195 | 196 | double Julian::calculate_time(unsigned int hour, unsigned int minute, unsigned int second, unsigned int ms) { 197 | double result = (((double) hour) / 24) + (((double) minute) / 1440) + 198 | (((double) second) / 86400) + (((double) ms) / 86400000); 199 | return result; 200 | } 201 | 202 | DayOfWeek Julian::Weekday(int minutes_west_utc) const { 203 | double adjJulian = _julian - (((double) minutes_west_utc) / 1440); 204 | int J = (int) (adjJulian + 0.5); 205 | 206 | DayOfWeek result = static_cast((J + 1) % 7); 207 | return result; 208 | } 209 | 210 | const int y = 4716; 211 | const int v = 3; 212 | const int j = 1401; 213 | const int u = 5; 214 | const int m = 2; 215 | const int s = 153; 216 | const int n = 12; 217 | const int w = 2; 218 | const int r = 4; 219 | const int B = 274277; 220 | const int p = 1461; 221 | const int C = -38; 222 | 223 | GregorianBreakdown Julian::to_gregorian(int minutes_west_utc) const { 224 | GregorianBreakdown result; 225 | 226 | double time; 227 | if (_time_type != TimeType::Time) { 228 | double adjJulian = _julian - (((double) minutes_west_utc) / 1440); 229 | int J = (int) (adjJulian + 0.5); 230 | int f = J + j; 231 | f = f + ((int) ((((int) (4 * J + B) / 146097) * 3) / 4)) + C; 232 | int e = r * f + v; 233 | int g = (int) ((e % p) / r); 234 | int h = u * g + w; 235 | result.day = ((int)((h % s) / u)) + 1; 236 | result.month = ((((int) h / s) + m) % n) + 1; 237 | result.year = ((int) e/p) - y + ((int)((n + m - result.month) / n)); 238 | 239 | time = adjJulian + .5; 240 | } else { 241 | time = _julian - (((double) minutes_west_utc) / 1440); 242 | result.day = (int) time; 243 | } 244 | 245 | time -= (int)time; 246 | double subms = (0.5 / 86400000); 247 | 248 | time = time * 24; 249 | subms = subms * 24; 250 | result.hour = (int) (time + subms); 251 | if (((int) time) != ((int) (time + subms))) { 252 | time -= (int)(time + subms); 253 | subms = 0; 254 | } else { 255 | time -= (int)(time + subms); 256 | } 257 | 258 | time = time * 60; 259 | subms = subms * 60; 260 | result.minute = (int) (time + subms); 261 | if (((int) time) != ((int) (time + subms))) { 262 | time -= (int)(time + subms); 263 | subms = 0; 264 | } else { 265 | time -= (int)(time + subms); 266 | } 267 | 268 | time = time * 60; 269 | subms = subms * 60; 270 | result.second = (int) (time + subms); 271 | if (((int) time) != ((int) (time + subms))) { 272 | time -= (int)(time + subms); 273 | subms = 0; 274 | } else { 275 | time -= (int)(time + subms); 276 | } 277 | 278 | time = time * 1000; 279 | subms = subms * 1000; 280 | result.millisecond = (int) (time + subms); 281 | 282 | result.minutes_west_utc = minutes_west_utc; 283 | result.time_type = _time_type; 284 | return result; 285 | } 286 | 287 | Julian operator+(const Julian &left, const Julian &right) { 288 | TimeType time_type = TimeType::DateTime; 289 | 290 | if (right.Type() != TimeType::Time) { 291 | throw JulianException("In order to add - the right hand side value must be a Time"); 292 | } 293 | 294 | if (left.Type() == TimeType::Time) { 295 | time_type = TimeType::Time; 296 | } 297 | 298 | return Julian(left.Value() + right.Value(), time_type); 299 | } 300 | 301 | Julian operator+(const Julian &left, double right) { 302 | TimeType time_type = TimeType::DateTime; 303 | 304 | if (left.Type() == TimeType::Time) { 305 | time_type = TimeType::Time; 306 | } 307 | 308 | return Julian(left.Value() + right, time_type); 309 | } 310 | 311 | Julian operator-(const Julian &left, const Julian &right) { 312 | TimeType time_type = TimeType::DateTime; 313 | 314 | if (right.Type() != TimeType::Time) { 315 | throw JulianException("In order to subtract - the right hand side value must be a Time"); 316 | } 317 | 318 | if (left.Type() == TimeType::Time) { 319 | time_type = TimeType::Time; 320 | } 321 | 322 | return Julian(left.Value() - right.Value(), time_type); 323 | } 324 | 325 | Julian operator-(const Julian &left, double right) { 326 | TimeType time_type = TimeType::DateTime; 327 | 328 | if (left.Type() == TimeType::Time) { 329 | time_type = TimeType::Time; 330 | } 331 | 332 | return Julian(left.Value() - right, time_type); 333 | } 334 | 335 | bool operator<(const Julian &left, const Julian &right) { 336 | if (left.Type() != right.Type()) { 337 | return left.Type() < right.Type(); 338 | } 339 | 340 | return left.Value() < right.Value(); 341 | } 342 | 343 | std::ostream &operator<<(std::ostream &out, const Julian &julian) { 344 | GregorianBreakdown bd = julian.to_gregorian(0); 345 | out << bd; 346 | return out; 347 | } 348 | 349 | } 350 | 351 | -------------------------------------------------------------------------------- /Julian.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace MySQLWrap { 8 | class JulianException : std::exception { 9 | public: 10 | JulianException(const std::string &msg) : _message(msg) {} 11 | virtual ~JulianException() throw() {} 12 | virtual const char *what() const throw() { return _message.c_str(); } 13 | private: 14 | std::string _message; 15 | }; 16 | 17 | enum class TimeType { Date, Time, DateTime }; 18 | struct GregorianBreakdown { 19 | int year; unsigned int month; unsigned int day; 20 | unsigned int hour; unsigned int minute; unsigned int second; unsigned int millisecond; 21 | int minutes_west_utc; 22 | TimeType time_type; 23 | 24 | GregorianBreakdown(); 25 | GregorianBreakdown(int year, unsigned int month, unsigned int day, 26 | unsigned int hour, unsigned int minute, unsigned int second, unsigned int ms, int minutes_west_utc); 27 | GregorianBreakdown(int year, unsigned int month, unsigned int day); 28 | GregorianBreakdown(unsigned int day, unsigned int hour, unsigned int minute, unsigned int second, unsigned int ms); 29 | GregorianBreakdown(const MYSQL_TIME &time, int minutes_west_utc); 30 | MYSQL_TIME to_mysql_time() const; 31 | }; 32 | 33 | enum class DayOfWeek : uint8_t { Sunday = 0, Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6 }; 34 | 35 | std::ostream &operator<<(std::ostream &out, const GregorianBreakdown &gb); 36 | 37 | class Julian { 38 | public: 39 | Julian(); 40 | Julian(double value, TimeType time_type); 41 | Julian(int year, unsigned int month, unsigned int day, unsigned int hour, unsigned int minute, unsigned int second, unsigned int ms); 42 | Julian(int year, unsigned int month, unsigned int day); 43 | Julian(unsigned int day, unsigned int hour, unsigned int minute, unsigned int second, unsigned int ms); 44 | Julian(const GregorianBreakdown &gb); 45 | Julian(const Julian &) = default; 46 | Julian &operator=(const Julian &) = default; 47 | 48 | double Value() const; 49 | TimeType Type() const; 50 | GregorianBreakdown to_gregorian(int minutes_west_utc) const; 51 | DayOfWeek Weekday(int minutes_west_utc) const; 52 | 53 | protected: 54 | static double calculate_utc(int year, unsigned int month, unsigned int day, 55 | unsigned int hour, unsigned int minute, unsigned int second, unsigned int ms); 56 | 57 | static double calculate_jdn(int year, unsigned int month, unsigned int day); 58 | static double calculate_time(unsigned int hour, unsigned int minute, unsigned int second, unsigned int ms); 59 | private: 60 | double _julian; 61 | TimeType _time_type; 62 | }; 63 | 64 | std::ostream &operator<<(std::ostream &out, const Julian &julian); 65 | bool operator<(const Julian &left, const Julian &right); 66 | Julian operator+(const Julian &left, const Julian &right); 67 | Julian operator+(const Julian &left, double right); 68 | Julian operator-(const Julian &left, const Julian &right); 69 | Julian operator-(const Julian &left, double right); 70 | } 71 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # These are the variables you need to edit when files are added / removed 3 | # 4 | # PROGRAM_SOURCE is the name of the .cpp file holding the main method (including the .cpp suffix) 5 | # LIBRARY_NAME is the name of the library file (do in include any prefixes or suffixes here) 6 | # LIBRARY_SOURCES are the names of the .cpp files that comprise the library (including the .cpp suffix) 7 | # LIBRARY_TYPE is either static (for an archive) or dynamic (for a dll/shared object library) 8 | # DELIVERY_FOLDER is the name of the folder the deliveries should be copied to 9 | # INTERMEDIATE_FOLDER is the name of the folder to contain the build files (do not include the ending /). 10 | # 11 | 12 | PROGRAM_SOURCE := program.cpp bug.cpp 13 | TEST_PROGRAM_SOURCE := test.cpp 14 | TEST_EMBEDDED_SOURCE := testemb.cpp 15 | LIBRARY_NAME := dbwrap 16 | LIBRARY_SOURCES := DatabaseException.cpp Database.cpp Statement.cpp ParamBuffer.cpp Binary.cpp AdhocStatement.cpp AdhocParameter.cpp Julian.cpp IStatement.cpp 17 | TEST_LIBRARY_SOURCES := UTFail.cpp TestNullable.cpp TestBinary.cpp TestDatabaseException.cpp TestDatabase.cpp TestImport.cpp TestJulian.cpp TestAdhocParameter.cpp 18 | LIBRARY_TYPE := dynamic 19 | DELIVERY_FOLDER := bin 20 | INTERMEDIATE_FOLDER := build 21 | EMBEDDED := 22 | DYLD_LIBRARY_PATH = /usr/local/mysql/lib 23 | export DYLD_LIBRARY_PATH 24 | #LC_ALL = C 25 | #export LC_ALL 26 | 27 | # 28 | # OS specific suffixes, prefixes, and switches used by make 29 | # 30 | # FOR OS/X: CPP=.cpp, OBJ=.o, DYS=.dylib, DYP=lib, ARS=.a, ARP=lib, EXE=, DEP=.d, USE_RPATH=yes, USE_INSTALLDIR=yes 31 | # FOR WIN: CPP=.cpp, OBJ=.obj DYS=.dll, DYP=, ARS=.a, ARP=, EXE=.exe, DEP=.d, USE_RPATH=no, USE_INSTALLDIR=no 32 | # FOR Linux: CPP=.cpp, OBJ=.o, DYS=.so, DYP=lib, ARS=.a, ARP=lib, EXE=, DEP=.d, USE_RPATH=yes, USE_INSTALLDIR=no 33 | # Note - Linux is untested at this time 34 | # 35 | 36 | CPP := .cpp 37 | OBJ := .o 38 | DYS := .dylib 39 | DYP := lib 40 | ARS := .a 41 | ARP := lib 42 | EXE := 43 | DEP := .d 44 | USE_RPATH := yes 45 | USE_INSTALLDIR := yes 46 | G++ := clang++ --std=gnu++11 -mmacosx-version-min=10.8 --stdlib=libstdc++ 47 | 48 | # 49 | # Variables used internally by make 50 | # 51 | 52 | ifeq ($(INTERMEDIATE_FOLDER),) 53 | INTERMEDIATE_FOLDER := . 54 | endif 55 | 56 | INTERMEDIATE_FOLDER := $(addsuffix /,$(INTERMEDIATE_FOLDER)) 57 | 58 | PROGRAM_OBJECT := $(addprefix $(INTERMEDIATE_FOLDER),$(PROGRAM_SOURCE:$(CPP)=$(OBJ))) 59 | TEST_PROGRAM_OBJECT := $(addprefix $(INTERMEDIATE_FOLDER),$(TEST_PROGRAM_SOURCE:$(CPP)=$(OBJ))) 60 | TEST_EMBEDDED_OBJECT := $(addprefix $(INTERMEDIATE_FOLDER),$(TEST_EMBEDDED_SOURCE:$(CPP)=$(OBJ))) 61 | PROGRAM_BINARY := $(PROGRAM_OBJECT:$(OBJ)=$(EXE)) 62 | TEST_PROGRAM_BINARY := $(TEST_PROGRAM_OBJECT:$(OBJ)=$(EXE)) 63 | TEST_EMBEDDED_BINARY := $(TEST_EMBEDDED_OBJECT:$(OBJ)=$(EXE)) 64 | LIBRARY_OBJECTS := $(addprefix $(INTERMEDIATE_FOLDER),$(LIBRARY_SOURCES:$(CPP)=$(OBJ))) 65 | TEST_LIBRARY_OBJECTS := $(addprefix $(INTERMEDIATE_FOLDER),$(TEST_LIBRARY_SOURCES:$(CPP)=$(OBJ))) 66 | DYNAMIC_LIBRARY := $(addprefix $(INTERMEDIATE_FOLDER), $(DYP)$(LIBRARY_NAME)$(DYS)) 67 | STATIC_LIBRARY := $(addprefix $(INTERMEDIATE_FOLDER), $(ARP)$(LIBRARY_NAME)$(ARS)) 68 | 69 | BUG_REPORT_BINARY := $(addprefix $(INTERMEDIATE_FOLDER), bug) 70 | 71 | ifeq ($(LIBRARY_NAME),) 72 | LIBRARY_TYPE := 73 | endif 74 | 75 | ifeq ($(LIBRARY_TYPE),static) 76 | LIBRARY_FILE := $(addprefix $(INTERMEDIATE_FOLDER),$(ARP)$(LIBRARY_NAME)$(ARS)) 77 | ARCHIVE_DEPEND := $(LIBRARY_FILE:$(ARS)=$(DEP)) 78 | INSTALLIR := 79 | LIBRARY_SWITCH := -static -l$(LIBRARY_NAME) 80 | LIBRARY_SWITCH_TEST := -static -l$(LIBRARY_NAME) 81 | else 82 | ifeq ($(LIBRARY_TYPE),dynamic) 83 | LIBRARY_FILE := $(addprefix $(INTERMEDIATE_FOLDER),$(DYP)$(LIBRARY_NAME)$(DYS)) 84 | ARCHIVE_DEPEND := $(LIBRARY_FILE:$(DYS)=$(DEP)) 85 | ifeq ($(USE_INSTALLDIR),yes) 86 | INSTALLDIR := -install_name @rpath/$(DYP)$(LIBRARY_NAME)$(DYS) 87 | else 88 | INSTALLDIR := 89 | endif 90 | LIBRARY_SWITCH := -shared -l$(LIBRARY_NAME) 91 | LIBRARY_SWITCH_TEST := -l$(LIBRARY_NAME) 92 | else 93 | LIBRARY_FILE := 94 | ACHIVE_DEPEND := 95 | INSTALLIR := 96 | LIBRARY_SWITCH := $(LIBRARY_OBJECTS) 97 | LIBRARY_SWITCH_TEST := $(LIBRARY_OBJECTS) 98 | endif 99 | endif 100 | 101 | PROGRAM_DEPEND := $(PROGRAM_OBJECT:$(OBJ)=$(DEP)) 102 | TEST_PROGRAM_DEPEND := $(TEST_PROGRAM_OBJECT:$(OBJ)=$(DEP)) 103 | TEST_EMBEDDED_DEPEND := $(TEST_PROGRAM_OBJECT:$(OBJ)=$(DEP)) 104 | LIBRARY_DEPEND := $(LIBRARY_OBJECTS:$(OBJ)=$(DEP)) 105 | 106 | CPP_INCDIR := ${shell /usr/local/mysql/bin/mysql_config --cflags} 107 | CPP_RPATH := -Wl,-rpath,/usr/local/mysql/lib 108 | 109 | MYSQL_EMBEDDED_LIBDIR := ${shell /usr/local/mysql/bin/mysql_config --libmysqld-libs}-debug 110 | MYSQL_CLIENT_LIBDIR := ${shell /usr/local/mysql/bin/mysql_config --libs_r} 111 | 112 | ifeq ($(EMBEDDED),embedded) 113 | CPP_LIBDIR := $(MYSQL_EMBEDDED_LIBDIR) 114 | else 115 | CPP_LIBDIR := $(MYSQL_CLIENT_LIBDIR) 116 | endif 117 | 118 | CPP11_LIB := 119 | 120 | 121 | ifeq ($(LIBRARY_TYPE),dynamic) 122 | ifeq ($(USE_RPATH),yes) 123 | RPATH := -Wl,-rpath,. -Wl,-rpath,$(INTERMEDIATE_FOLDER) -Wl,-rpath,$(DELIVERY_FOLDER) 124 | else 125 | RPATH := 126 | endif 127 | endif 128 | 129 | COVERAGE := -g -O0 -ftest-coverage -fprofile-arcs 130 | COVERAGELIB := -lprofile_rt 131 | 132 | # end of variables - start of targets 133 | 134 | .PHONY: all clean deliver cleanall run cleancover cover runtest 135 | .PRECIOUS: $(LIBRARY_OBJECTS) 136 | 137 | all: $(PROGRAM_BINARY) $(TEST_PROGRAM_BINARY) $(STATIC_LIBRARY) $(DYNAMIC_LIBRARY) $(TEST_EMBEDDED_BINARY) 138 | 139 | cleancover: 140 | rm -rf cover 141 | rm -f $(INTERMEDIATE_FOLDER)*.gcda $(INTERMEDIATE_FOLDER)*.gcno 142 | 143 | clean: 144 | rm -f $(PROGRAM_OBJECT) $(PROGRAM_BINARY) $(LIBRARY_OBJECTS) $(LIBRARY_FILE) $(TEST_PROGRAM_BINARY) $(TEST_PROGRAM_OBJECT) $(TEST_LIBRARY_OBJECTS) 145 | rm -f $(TEST_EMBEDDED_BINARY) $(TEST_EMBEDDED_OBJECT) 146 | rm -f $(DELIVERY_FOLDER)/* 147 | rm -f $(STATIC_LIBRARY) $(DYNAMIC_LIBRARY) 148 | 149 | cleanall: clean cleancover 150 | rm -f $(INTERMEDIATE_FOLDER)*.d 151 | 152 | cover: runtest 153 | lcov -a cover/test.info -a cover/testemb.info -o cover/testagg.info 154 | genhtml -o cover/mysqlwrap cover/testagg.info 155 | 156 | deliver: all 157 | @mkdir -p $(DELIVERY_FOLDER) 158 | cp -f $(PROGRAM_BINARY) $(DELIVERY_FOLDER) 159 | ifeq ($(LIBRARY_TYPE),dynamic) 160 | cp -f $(LIBRARY_FILE) $(DELIVERY_FOLDER) 161 | endif 162 | 163 | run: deliver 164 | @echo $(DYLD_LIBRARY_PATH) 165 | @for progs in $(PROGRAM_BINARY); do \ 166 | $$progs; \ 167 | echo "-----------------------------\n"; \ 168 | done 169 | 170 | runtest: all $(TEST_PROGRAM_BINARY) $(TEST_EMBEDDED_BINARY) 171 | @mkdir -p cover 172 | @for progs in $(TEST_PROGRAM_BINARY); do \ 173 | $$progs ; \ 174 | lcov -o cover/test.info -c -d . ; \ 175 | done 176 | @for progs in $(TEST_EMBEDDED_BINARY); do \ 177 | $$progs $(EMBEDDED) ; \ 178 | lcov -o cover/testemb.info -c -d . ; \ 179 | done 180 | 181 | runbug: $(BUG_REPORT_BINARY) 182 | @for progs in $(BUG_REPORT_BINARY); do \ 183 | $$progs $(EMBEDDED); \ 184 | done 185 | 186 | createdb: 187 | @rm -rf $(HOME)/mysql 188 | @mkdir $(HOME)/mysql 189 | @mkdir $(HOME)/mysql/data 190 | @mkdir $(HOME)/mysql/log 191 | @mkdir $(HOME)/mysql/tmp 192 | @mkdir $(HOME)/mysql/plugins 193 | sudo /usr/local/mysql/scripts/mysql_install_db --user=`whoami` --basedir=/usr/local/mysql --datadir=$(HOME)/mysql/data --plugin-dir=$(HOME)/mysql/plugins 194 | 195 | 196 | $(INTERMEDIATE_FOLDER)%$(OBJ): %$(CPP) 197 | $(G++) -Wall $(COVERAGE) -c $< $(CPP_INCDIR) -o $@ 198 | @$(G++) -MM $< $(CPP_INCDIR) > $(@:$(OBJ)=$(DEP)) 199 | @mv -f $(@:$(OBJ)=$(DEP)) $(@:$(OBJ)=$(DEP).tmp) 200 | @sed -e 's|.*:|$(@):|' < $(@:$(OBJ)=$(DEP).tmp) > $(@:$(OBJ)=$(DEP)) 201 | @sed -e 's/.*://' -e 's/\\$$//' < $(@:$(OBJ)=$(DEP).tmp) | fmt -1 | \ 202 | sed -e 's/^ *//' -e 's/$$/:/' >> $(@:$(OBJ)=$(DEP)) 203 | @rm -f $(@:$(OBJ)=$(DEP).tmp) 204 | 205 | ifeq ($(LIBRARY_FILE),) 206 | $(PROGRAM_BINARY): $(LIBRARY_OBJECTS) 207 | endif 208 | 209 | $(PROGRAM_BINARY): $(LIBRARY_FILE) 210 | 211 | $(PROGRAM_BINARY): % : %$(OBJ) 212 | $(G++) -Wall $(<) $(CPP_INCDIR) $(LIBRARY_SWITCH) -o $(@) $(CPP_LIBDIR) $(CPP11_LIB) -L$(INTERMEDIATE_FOLDER) $(RPATH) $(CPP_RPATH) $(COVERAGELIB) 213 | @$(G++) -MM $(CPP_INCDIR) $(notdir $(<:$(OBJ)=$(CPP))) > $(<:$(OBJ)=$(DEP)) 214 | @mv $(<:$(OBJ)=$(DEP)) $(<:$(OBJ)=.tmp) 215 | @sed "s|^.*:|$(@):|" $(<:$(OBJ)=.tmp) > $(<:$(OBJ)=$(DEP)) 216 | @rm $(<:$(OBJ)=.tmp) 217 | 218 | $(TEST_EMBEDDED_BINARY): $(STATIC_LIBRARY) 219 | 220 | $(TEST_EMBEDDED_BINARY): $(TEST_LIBRARY_OBJECTS) 221 | 222 | $(TEST_EMBEDDED_BINARY): % : %$(OBJ) 223 | $(G++) -Wall -g -O0 $(<) $(LIBRARY_SWITCH_TEST) $(TEST_LIBRARY_OBJECTS) -o $(@) $(MYSQL_EMBEDDED_LIBDIR) -L$(INTERMEDIATE_FOLDER) $(RPATH) $(COVERAGELIB) $(CPP11_LIB) 224 | @$(G++) -MM $(CPP_INCDIR) $(notdir $(<:$(OBJ)=$(CPP))) > $(<:$(OBJ)=$(DEP)) 225 | @mv $(<:$(OBJ)=$(DEP)) $(<:$(OBJ)=.tmp) 226 | @sed "s|^.*:|$(@):|" $(<:$(OBJ)=.tmp) > $(<:$(OBJ)=$(DEP)) 227 | @rm $(<:$(OBJ)=.tmp) 228 | 229 | $(TEST_PROGRAM_BINARY): $(STATIC_LIBRARY) 230 | 231 | $(TEST_PROGRAM_BINARY): $(TEST_LIBRARY_OBJECTS) 232 | 233 | $(TEST_PROGRAM_BINARY): % : %$(OBJ) 234 | $(G++) -Wall -g -O0 $(<) $(LIBRARY_SWITCH_TEST) $(TEST_LIBRARY_OBJECTS) -o $(@) $(MYSQL_CLIENT_LIBDIR) -L$(INTERMEDIATE_FOLDER) $(RPATH) $(COVERAGELIB) $(CPP11_LIB) 235 | @$(G++) -MM $(CPP_INCDIR) $(notdir $(<:$(OBJ)=$(CPP))) > $(<:$(OBJ)=$(DEP)) 236 | @mv $(<:$(OBJ)=$(DEP)) $(<:$(OBJ)=.tmp) 237 | @sed "s|^.*:|$(@):|" $(<:$(OBJ)=.tmp) > $(<:$(OBJ)=$(DEP)) 238 | @rm $(<:$(OBJ)=.tmp) 239 | 240 | %$(ARS): $(LIBRARY_OBJECTS) 241 | ar -crs $(@) $(LIBRARY_OBJECTS) 242 | @$(G++) -MM $(CPP_INCDIR) $(LIBRARY_SOURCES) > $(ARCHIVE_DEPEND) 243 | @mv $(ARCHIVE_DEPEND) $(ARCHIVE_DEPEND:$(DEP)=.tmp) 244 | @sed "s|^.*:|$(@):|" $(ARCHIVE_DEPEND:$(DEP)=.tmp) > $(ARCHIVE_DEPEND) 245 | @rm $(ARCHIVE_DEPEND:$(DEP)=.tmp) 246 | 247 | %$(DYS): $(LIBRARY_OBJECTS) 248 | $(G++) -Wall $(COVERAGE) -shared $(CPP_INCDIR) $(LIBRARY_OBJECTS) $(COVERAGELIB) -o $(@) $(CPP_LIBDIR) $(CPP11_LIB) $(INSTALLDIR) $(COVERAGELIB) 249 | @$(G++) -MM $(CPP_INCDIR) $(LIBRARY_SOURCES) > $(ARCHIVE_DEPEND) 250 | @mv $(ARCHIVE_DEPEND) $(ARCHIVE_DEPEND:$(DEP)=.tmp) 251 | @sed "s|^.*:|$(@):|" $(ARCHIVE_DEPEND:$(DEP)=.tmp) > $(ARCHIVE_DEPEND) 252 | @rm $(ARCHIVE_DEPEND:$(DEP)=.tmp) 253 | 254 | 255 | -include $(PROGRAM_DEPEND) $(ARCHIVE_DEPEND) $(LIBRARY_DEPEND) $(TEST_PROGRAM_DEPEND) 256 | 257 | -------------------------------------------------------------------------------- /Makefile.bug: -------------------------------------------------------------------------------- 1 | # 2 | # These are the variables you need to edit when files are added / removed 3 | # 4 | # PROGRAM_SOURCE is the name of the .cpp file holding the main method (including the .cpp suffix) 5 | # LIBRARY_NAME is the name of the library file (do in include any prefixes or suffixes here) 6 | # LIBRARY_SOURCES are the names of the .cpp files that comprise the library (including the .cpp suffix) 7 | # LIBRARY_TYPE is either static (for an archive) or dynamic (for a dll/shared object library) 8 | # DELIVERY_FOLDER is the name of the folder the deliveries should be copied to 9 | # INTERMEDIATE_FOLDER is the name of the folder to contain the build files (do not include the ending /). 10 | # 11 | 12 | PROGRAM_SOURCE := bug.cpp 13 | TEST_PROGRAM_SOURCE := 14 | LIBRARY_NAME := dbwrap 15 | LIBRARY_SOURCES := 16 | TEST_LIBRARY_SOURCES := 17 | LIBRARY_TYPE := 18 | DELIVERY_FOLDER := bin 19 | INTERMEDIATE_FOLDER := build 20 | EMBEDDED := embedded 21 | DYLD_LIBRARY_PATH = /usr/local/mysql/lib 22 | export DYLD_LIBRARY_PATH 23 | 24 | # 25 | # OS specific suffixes, prefixes, and switches used by make 26 | # 27 | # FOR OS/X: CPP=.cpp, OBJ=.o, DYS=.dylib, DYP=lib, ARS=.a, ARP=lib, EXE=, DEP=.d, USE_RPATH=yes, USE_INSTALLDIR=yes 28 | # FOR WIN: CPP=.cpp, OBJ=.obj DYS=.dll, DYP=, ARS=.a, ARP=, EXE=.exe, DEP=.d, USE_RPATH=no, USE_INSTALLDIR=no 29 | # FOR Linux: CPP=.cpp, OBJ=.o, DYS=.so, DYP=lib, ARS=.a, ARP=lib, EXE=, DEP=.d, USE_RPATH=yes, USE_INSTALLDIR=no 30 | # Note - Linux is untested at this time 31 | # 32 | 33 | CPP := .cpp 34 | OBJ := .o 35 | DYS := .dylib 36 | DYP := lib 37 | ARS := .a 38 | ARP := lib 39 | EXE := 40 | DEP := .d 41 | USE_RPATH := yes 42 | USE_INSTALLDIR := yes 43 | 44 | # 45 | # Variables used internally by make 46 | # 47 | 48 | ifeq ($(INTERMEDIATE_FOLDER),) 49 | INTERMEDIATE_FOLDER := . 50 | endif 51 | 52 | INTERMEDIATE_FOLDER := $(addsuffix /,$(INTERMEDIATE_FOLDER)) 53 | 54 | PROGRAM_OBJECT := $(addprefix $(INTERMEDIATE_FOLDER),$(PROGRAM_SOURCE:$(CPP)=$(OBJ))) 55 | TEST_PROGRAM_OBJECT := $(addprefix $(INTERMEDIATE_FOLDER),$(TEST_PROGRAM_SOURCE:$(CPP)=$(OBJ))) 56 | PROGRAM_BINARY := $(PROGRAM_OBJECT:$(OBJ)=$(EXE)) 57 | TEST_PROGRAM_BINARY := $(TEST_PROGRAM_OBJECT:$(OBJ)=$(EXE)) 58 | LIBRARY_OBJECTS := $(addprefix $(INTERMEDIATE_FOLDER),$(LIBRARY_SOURCES:$(CPP)=$(OBJ))) 59 | TEST_LIBRARY_OBJECTS := $(addprefix $(INTERMEDIATE_FOLDER),$(TEST_LIBRARY_SOURCES:$(CPP)=$(OBJ))) 60 | 61 | BUG_REPORT_BINARY := $(addprefix $(INTERMEDIATE_FOLDER), bug) 62 | 63 | ifeq ($(LIBRARY_NAME),) 64 | LIBRARY_TYPE := 65 | endif 66 | 67 | ifeq ($(LIBRARY_TYPE),static) 68 | LIBRARY_FILE := $(addprefix $(INTERMEDIATE_FOLDER),$(ARP)$(LIBRARY_NAME)$(ARS)) 69 | ARCHIVE_DEPEND := $(LIBRARY_FILE:$(ARS)=$(DEP)) 70 | INSTALLIR := 71 | LIBRARY_SWITCH := -l$(LIBRARY_NAME) 72 | else 73 | ifeq ($(LIBRARY_TYPE),dynamic) 74 | LIBRARY_FILE := $(addprefix $(INTERMEDIATE_FOLDER),$(DYP)$(LIBRARY_NAME)$(DYS)) 75 | ARCHIVE_DEPEND := $(LIBRARY_FILE:$(DYS)=$(DEP)) 76 | ifeq ($(USE_INSTALLDIR),yes) 77 | INSTALLDIR := -install_name @rpath/$(DYP)$(LIBRARY_NAME)$(DYS) 78 | else 79 | INSTALLDIR := 80 | endif 81 | LIBRARY_SWITCH := -l$(LIBRARY_NAME) 82 | else 83 | LIBRARY_FILE ;= 84 | ACHIVE_DEPEND ;= 85 | INSTALLIR := 86 | LIBRARY_SWITCH := $(LIBRARY_OBJECTS) 87 | endif 88 | endif 89 | 90 | PROGRAM_DEPEND := $(PROGRAM_OBJECT:$(OBJ)=$(DEP)) 91 | TEST_PROGRAM_DEPEND := $(TEST_PROGRAM_OBJECT:$(OBJ)=$(DEP)) 92 | LIBRARY_DEPEND := $(LIBRARY_OBJECTS:$(OBJ)=$(DEP)) 93 | 94 | CPP_INCDIR := ${shell /usr/local/mysql/bin/mysql_config --cflags} 95 | CPP_RPATH := 96 | 97 | ifeq ($(EMBEDDED),embedded) 98 | CPP_LIBDIR := ${shell /usr/local/mysql/bin/mysql_config --libmysqld-libs} 99 | else 100 | CPP_LIBDIR := ${shell /usr/local/mysql/bin/mysql_config --libs_r} 101 | endif 102 | 103 | 104 | ifeq ($(LIBRARY_TYPE),dynamic) 105 | ifeq ($(USE_RPATH),yes) 106 | RPATH := -Wl,-rpath,. -Wl,-rpath,$(INTERMEDIATE_FOLDER) -Wl,-rpath,$(DELIVERY_FOLDER) 107 | else 108 | RPATH := 109 | endif 110 | endif 111 | 112 | COVERAGE := -g3 -O0 113 | COVERAGELIB := 114 | 115 | # end of variables - start of targets 116 | 117 | .PHONY: all clean deliver cleanall run 118 | .PRECIOUS: $(LIBRARY_OBJECTS) 119 | 120 | all: $(PROGRAM_BINARY) $(TEST_PROGRAM_BINARY) 121 | 122 | clean: 123 | rm -f $(PROGRAM_OBJECT) $(PROGRAM_BINARY) $(LIBRARY_OBJECTS) $(LIBRARY_FILE) $(TEST_PROGRAM_BINARY) $(TEST_PROGRAM_OBJECT) $(TEST_LIBRARY_OBJECTS) 124 | rm -f $(DELIVERY_FOLDER)/* 125 | 126 | 127 | cleanall: clean 128 | rm -f $(INTERMEDIATE_FOLDER)*.d 129 | 130 | deliver: all 131 | @mkdir -p $(DELIVERY_FOLDER) 132 | cp -f $(PROGRAM_BINARY) $(DELIVERY_FOLDER) 133 | ifeq ($(LIBRARY_TYPE),dynamic) 134 | cp -f $(LIBRARY_FILE) $(DELIVERY_FOLDER) 135 | endif 136 | 137 | run: deliver 138 | @echo $(DYLD_LIBRARY_PATH) 139 | @for progs in $(PROGRAM_BINARY); do \ 140 | $$progs $(EMBEDDED); \ 141 | echo "-----------------------------\n"; \ 142 | done 143 | 144 | runtest: $(TEST_PROGRAM_BINARY) 145 | @for progs in $(TEST_PROGRAM_BINARY); do \ 146 | $$progs $(EMBEDDED) ; \ 147 | done 148 | 149 | runbug: $(BUG_REPORT_BINARY) 150 | @for progs in $(BUG_REPORT_BINARY); do \ 151 | $$progs $(EMBEDDED); \ 152 | done 153 | 154 | $(INTERMEDIATE_FOLDER)%$(OBJ): %$(CPP) 155 | g++ -Wall $(COVERAGE) -c $< $(CPP_INCDIR) -o $@ 156 | @g++ -MM $< $(CPP_INCDIR) > $(@:$(OBJ)=$(DEP)) 157 | @mv -f $(@:$(OBJ)=$(DEP)) $(@:$(OBJ)=$(DEP).tmp) 158 | @sed -e 's|.*:|$(@):|' < $(@:$(OBJ)=$(DEP).tmp) > $(@:$(OBJ)=$(DEP)) 159 | @sed -e 's/.*://' -e 's/\\$$//' < $(@:$(OBJ)=$(DEP).tmp) | fmt -1 | \ 160 | sed -e 's/^ *//' -e 's/$$/:/' >> $(@:$(OBJ)=$(DEP)) 161 | @rm -f $(@:$(OBJ)=$(DEP).tmp) 162 | 163 | ifeq ($(LIBRARY_FILE),) 164 | $(PROGRAM_BINARY): $(LIBRARY_OBJECTS) 165 | endif 166 | 167 | $(PROGRAM_BINARY): $(LIBRARY_FILE) 168 | 169 | $(PROGRAM_BINARY): % : %$(OBJ) 170 | g++ -Wall $(<) $(COVERAGE) $(CPP_INCDIR) $(LIBRARY_SWITCH) -o $(@) $(CPP_LIBDIR) -L$(INTERMEDIATE_FOLDER) $(RPATH) $(CPP_RPATH) 171 | @g++ -MM $(CPP_INCDIR) $(notdir $(<:$(OBJ)=$(CPP))) > $(<:$(OBJ)=$(DEP)) 172 | @mv $(<:$(OBJ)=$(DEP)) $(<:$(OBJ)=.tmp) 173 | @sed "s|^.*:|$(@):|" $(<:$(OBJ)=.tmp) > $(<:$(OBJ)=$(DEP)) 174 | @rm $(<:$(OBJ)=.tmp) 175 | 176 | $(TEST_PROGRAM_BINARY): $(LIBRARY_FILE) 177 | 178 | $(TEST_PROGRAM_BINARY): $(TEST_LIBRARY_OBJECTS) 179 | 180 | $(TEST_PROGRAM_BINARY): % : %$(OBJ) 181 | g++ -Wall $(COVERAGE) $(<) $(LIBRARY_SWITCH) $(COVERAGELIB) $(TEST_LIBRARY_OBJECTS) -o $(@) $(CPP_LIBDIR) -L$(INTERMEDIATE_FOLDER) $(RPATH) 182 | @g++ -MM $(CPP_INCDIR) $(notdir $(<:$(OBJ)=$(CPP))) > $(<:$(OBJ)=$(DEP)) 183 | @mv $(<:$(OBJ)=$(DEP)) $(<:$(OBJ)=.tmp) 184 | @sed "s|^.*:|$(@):|" $(<:$(OBJ)=.tmp) > $(<:$(OBJ)=$(DEP)) 185 | @rm $(<:$(OBJ)=.tmp) 186 | 187 | %$(ARS): $(LIBRARY_OBJECTS) 188 | ar -crs $(LIBRARY_FILE) $(LIBRARY_OBJECTS) 189 | @g++ -MM $(CPP_INCDIR) $(LIBRARY_SOURCES) > $(ARCHIVE_DEPEND) 190 | @mv $(ARCHIVE_DEPEND) $(ARCHIVE_DEPEND:$(DEP)=.tmp) 191 | @sed "s|^.*:|$(@):|" $(ARCHIVE_DEPEND:$(DEP)=.tmp) > $(ARCHIVE_DEPEND) 192 | @rm $(ARCHIVE_DEPEND:$(DEP)=.tmp) 193 | 194 | %$(DYS): $(LIBRARY_OBJECTS) 195 | g++ -Wall $(COVERAGE) -shared $(CPP_INCDIR) $(LIBRARY_OBJECTS) $(COVERAGELIB) -o $(@) $(CPP_LIBDIR) $(INSTALLDIR) 196 | @g++ -MM $(CPP_INCDIR) $(LIBRARY_SOURCES) > $(ARCHIVE_DEPEND) 197 | @mv $(ARCHIVE_DEPEND) $(ARCHIVE_DEPEND:$(DEP)=.tmp) 198 | @sed "s|^.*:|$(@):|" $(ARCHIVE_DEPEND:$(DEP)=.tmp) > $(ARCHIVE_DEPEND) 199 | @rm $(ARCHIVE_DEPEND:$(DEP)=.tmp) 200 | 201 | 202 | -include $(PROGRAM_DEPEND) $(ARCHIVE_DEPEND) $(LIBRARY_DEPEND) $(TEST_PROGRAM_DEPEND) 203 | 204 | -------------------------------------------------------------------------------- /Nullable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | namespace MySQLWrap { 10 | 11 | class NullableException { 12 | public: 13 | NullableException() {} 14 | friend ostream &operator<<(ostream &out, const NullableException &ne) { 15 | out << "Error dereferencing a Nullable type that does not have a value"; 16 | return out; 17 | } 18 | }; 19 | 20 | 21 | template 22 | class Nullable { 23 | public: 24 | Nullable() { 25 | _hasValue = false; 26 | } 27 | Nullable(T &value) { 28 | _storage = value; 29 | _hasValue = true; 30 | } 31 | Nullable(T const &value) { 32 | _storage = value; 33 | _hasValue = true; 34 | } 35 | 36 | Nullable(const Nullable ©) { 37 | _hasValue = copy._hasValue; 38 | if (_hasValue) { 39 | _storage = copy._storage; 40 | } 41 | } 42 | ~Nullable() { 43 | _hasValue = false; 44 | } 45 | 46 | Nullable &operator=(const T& value) { 47 | _storage = value; 48 | _hasValue = true; 49 | return *this; 50 | } 51 | 52 | Nullable &operator=(T& value) { 53 | _storage = value; 54 | _hasValue = true; 55 | return *this; 56 | } 57 | 58 | Nullable &operator=(const Nullable &equal) { 59 | _hasValue = equal._hasValue; 60 | if (_hasValue) { 61 | _storage = equal._storage; 62 | } 63 | return *this; 64 | } 65 | 66 | void ClearValue() { 67 | _hasValue = false; 68 | } 69 | 70 | bool HasValue() const { 71 | return _hasValue; 72 | } 73 | 74 | T const *operator->() const { 75 | if (! _hasValue) { 76 | throw NullableException(); 77 | } 78 | return &_storage; 79 | } 80 | 81 | const T& const_deref() const { 82 | if (! _hasValue) { 83 | throw NullableException(); 84 | } 85 | return _storage; 86 | } 87 | 88 | T& deref() { 89 | if (! _hasValue) { 90 | throw NullableException(); 91 | } 92 | return _storage; 93 | } 94 | 95 | T const operator *() const { 96 | if (! _hasValue) { 97 | throw NullableException(); 98 | } 99 | return _storage; 100 | } 101 | 102 | operator bool() const { 103 | return _hasValue; 104 | } 105 | 106 | bool operator ! () const { 107 | return (! _hasValue); 108 | } 109 | 110 | friend ostream &operator<<(ostream &out, const Nullable &value) { 111 | if (! value) { 112 | out << ""; 113 | } else { 114 | out << *value; 115 | } 116 | return out; 117 | } 118 | 119 | private: 120 | T _storage; 121 | bool _hasValue; 122 | }; 123 | 124 | template inline bool operator == (const Nullable &x, const Nullable &y) { 125 | if ((!x) && (!y)) return true; 126 | if ((!x) || (!y)) return false; 127 | return (*x == *y); 128 | } 129 | 130 | template inline bool operator != (const Nullable &x, const Nullable &y) { 131 | if ((!x) && (!y)) return false; 132 | if ((!x) || (!y)) return true; 133 | return (*x != *y); 134 | } 135 | 136 | template inline bool operator < (const Nullable &x, const Nullable &y) { 137 | if ((!x) && (!y)) return false; 138 | if (!x) return true; 139 | if (!y) return false; 140 | return (*x < *y); 141 | } 142 | 143 | template inline bool operator > (const Nullable &x, const Nullable &y) { 144 | if ((!x) && (!y)) return false; 145 | if (!x) return false; 146 | if (!y) return true; 147 | return (*x > *y); 148 | } 149 | 150 | template inline bool operator <= (const Nullable &x, const Nullable &y) { 151 | if ((!x) && (!y)) return true; 152 | if (!x) return true; 153 | if (!y) return false; 154 | return (*x <= *y); 155 | } 156 | 157 | template inline bool operator >= (const Nullable &x, const Nullable &y) { 158 | if ((!x) && (!y)) return true; 159 | if (!x) return false; 160 | if (!y) return true; 161 | return (*x >= *y); 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /ParamBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "ParamBuffer.h" 2 | #include "DatabaseException.h" 3 | 4 | #include 5 | using namespace std; 6 | 7 | namespace MySQLWrap { 8 | 9 | ParamBuffer::ParamBuffer(enum_field_types type, my_bool isUnsigned) { 10 | _type = type; 11 | _buffer = NULL; 12 | _bufferSize = 0; 13 | _bufferLength = 0; 14 | _isNull = 1; 15 | _isUnsigned = isUnsigned; 16 | } 17 | 18 | ParamBuffer::ParamBuffer(const std::type_info &info) { 19 | my_bool isUnsigned = false; 20 | 21 | if (info == typeid(std::string)) { 22 | _type = MYSQL_TYPE_STRING; 23 | } else if (info == typeid(Binary)) { 24 | _type = MYSQL_TYPE_BLOB; 25 | } else if (info == typeid(double)) { 26 | _type = MYSQL_TYPE_DOUBLE; 27 | } else if (info == typeid(float)) { 28 | _type = MYSQL_TYPE_FLOAT; 29 | } else if (info == typeid(char)) { 30 | _type = MYSQL_TYPE_TINY; 31 | } else if (info == typeid(unsigned char)) { 32 | _type = MYSQL_TYPE_TINY; 33 | isUnsigned = true; 34 | } else if (info == typeid(short)) { 35 | _type = MYSQL_TYPE_SHORT; 36 | } else if (info == typeid(unsigned short)) { 37 | _type = MYSQL_TYPE_SHORT; 38 | isUnsigned = true; 39 | } else if (info == typeid(int)) { 40 | _type = MYSQL_TYPE_LONG; 41 | } else if (info == typeid (unsigned int)) { 42 | _type = MYSQL_TYPE_LONG; 43 | isUnsigned = true; 44 | } 45 | 46 | _buffer = NULL; 47 | _bufferSize = 0; 48 | _bufferLength = 0; 49 | _isNull = 1; 50 | _isUnsigned = isUnsigned; 51 | } 52 | 53 | ParamBuffer::ParamBuffer(const std::string &str) : ParamBuffer(str, str.length()) { 54 | } 55 | 56 | ParamBuffer::ParamBuffer(const std::string &str, size_t maxSize) { 57 | if (str.size() > maxSize) { 58 | throw DatabaseException("Error in ParamBuffer::ParamBuffer(string)", 0, "----", "length of str parameter is greater than maxSize"); 59 | } 60 | 61 | if (maxSize > 0) { 62 | _buffer = malloc(maxSize); 63 | _bufferSize = maxSize; 64 | _bufferLength = str.length(); 65 | _type = MYSQL_TYPE_STRING; 66 | _isNull = 0; 67 | } else { 68 | _buffer = NULL; 69 | _bufferSize = 0; 70 | _bufferLength = 0; 71 | _type = MYSQL_TYPE_STRING; 72 | _isNull = 1; 73 | } 74 | 75 | memcpy(_buffer, str.data(), str.length()); 76 | } 77 | 78 | void *ParamBuffer::Buffer() const { 79 | return _buffer; 80 | } 81 | 82 | enum_field_types ParamBuffer::BufferType() const { 83 | return _type; 84 | } 85 | 86 | size_t ParamBuffer::BufferSize() const { 87 | return _bufferSize; 88 | } 89 | 90 | unsigned long *ParamBuffer::BufferLength() { 91 | return &_bufferLength; 92 | } 93 | 94 | my_bool *ParamBuffer::IsNull() { 95 | return &_isNull; 96 | } 97 | 98 | ParamBuffer::ParamBuffer(const char ch) { 99 | _bufferSize = sizeof (char); 100 | _buffer = malloc(_bufferSize); 101 | _bufferLength = _bufferSize; 102 | _type = MYSQL_TYPE_TINY; 103 | _isNull = 0; 104 | _isTruncated = 0; 105 | _isUnsigned = 0; 106 | 107 | *((char *)_buffer) = ch; 108 | } 109 | 110 | ParamBuffer::ParamBuffer(const unsigned char ch) { 111 | _bufferSize = sizeof (char); 112 | _buffer = malloc(_bufferSize); 113 | _bufferLength = _bufferSize; 114 | _type = MYSQL_TYPE_TINY; 115 | _isNull = 0; 116 | _isTruncated = 0; 117 | _isUnsigned = 1; 118 | 119 | *((char *)_buffer) = ch; 120 | } 121 | 122 | ParamBuffer::ParamBuffer(const short int i) { 123 | _buffer = malloc(sizeof(short int)); 124 | _bufferSize = sizeof (short int); 125 | _bufferLength = _bufferSize; 126 | _type = MYSQL_TYPE_SHORT; 127 | _isNull = 0; 128 | _isTruncated = 0; 129 | _isUnsigned = 0; 130 | 131 | *((short int *)_buffer) = i; 132 | } 133 | 134 | ParamBuffer::ParamBuffer(const unsigned short int i) { 135 | _buffer = malloc(sizeof(short int)); 136 | _bufferSize = sizeof (short int); 137 | _bufferLength = _bufferSize; 138 | _type = MYSQL_TYPE_SHORT; 139 | _isNull = 0; 140 | _isTruncated = 0; 141 | _isUnsigned = 1; 142 | 143 | *((short int *)_buffer) = i; 144 | } 145 | 146 | ParamBuffer::ParamBuffer(const float f) { 147 | _buffer = malloc(sizeof(float)); 148 | _bufferSize = sizeof (float); 149 | _bufferLength = _bufferSize; 150 | _type = MYSQL_TYPE_FLOAT; 151 | _isNull = 0; 152 | _isTruncated = 0; 153 | _isUnsigned = 1; 154 | 155 | *((float *)_buffer) = f; 156 | } 157 | 158 | ParamBuffer::ParamBuffer(const double d) { 159 | _buffer = malloc(sizeof(double)); 160 | _bufferSize = sizeof (double); 161 | _bufferLength = _bufferSize; 162 | _type = MYSQL_TYPE_FLOAT; 163 | _isNull = 0; 164 | _isTruncated = 0; 165 | _isUnsigned = 1; 166 | 167 | *((double *)_buffer) = d; 168 | } 169 | 170 | ParamBuffer::ParamBuffer(const int i) { 171 | _buffer = malloc(sizeof(int)); 172 | _bufferSize = sizeof(int); 173 | _bufferLength = sizeof(int); 174 | _type = MYSQL_TYPE_LONG; 175 | _isNull = 0; 176 | _isTruncated = 0; 177 | _isUnsigned = 0; 178 | 179 | *((int *)_buffer) = i; 180 | } 181 | 182 | ParamBuffer::ParamBuffer(const unsigned int i) { 183 | _buffer = malloc(sizeof(unsigned int)); 184 | _bufferSize = sizeof(unsigned int); 185 | _bufferLength = _bufferSize; 186 | _type = MYSQL_TYPE_LONG; 187 | _isNull = 0; 188 | _isTruncated = 0; 189 | _isUnsigned = 1; 190 | 191 | *((unsigned long *)_buffer) = i; 192 | } 193 | 194 | ParamBuffer::ParamBuffer(const Julian &julian) { 195 | _buffer = malloc(sizeof(MYSQL_TIME)); 196 | _bufferSize = sizeof(MYSQL_TIME); 197 | _bufferLength = _bufferSize; 198 | _type = MYSQL_TYPE_DATETIME; 199 | _isNull = 0; 200 | memset(_buffer, 0, sizeof(MYSQL_TIME)); 201 | 202 | if (julian.Value() > 0) { 203 | GregorianBreakdown gb = julian.to_gregorian(0); 204 | MYSQL_TIME tm = gb.to_mysql_time(); 205 | *((MYSQL_TIME *)_buffer) = tm; 206 | } 207 | } 208 | 209 | ParamBuffer::ParamBuffer(const Binary &data) { 210 | _bufferSize = data.BufferSize(); 211 | _buffer = malloc(_bufferSize); 212 | _bufferLength = data.BufferLength(); 213 | _type = MYSQL_TYPE_BLOB; 214 | _isNull = ((_buffer == NULL) ? 1 : 0); 215 | 216 | memcpy(_buffer, data.Buffer(), _bufferLength); 217 | } 218 | 219 | ParamBuffer::~ParamBuffer() { 220 | if (_buffer != NULL) { 221 | free(_buffer); 222 | } 223 | _buffer = NULL; 224 | _bufferSize = 0; 225 | _bufferLength = 0; 226 | _isNull = 1; 227 | } 228 | 229 | void ParamBuffer::ResizeBlob(size_t newSize) { 230 | if (_type != MYSQL_TYPE_BLOB) { 231 | throw DatabaseException("Error in ParamBuffer::ResizeBlob", 0, "----", "Datatype is not blob"); 232 | } 233 | 234 | if (_bufferSize != newSize) { 235 | free(_buffer); 236 | _buffer = malloc(newSize); 237 | _bufferSize = newSize; 238 | _bufferLength = 0; 239 | } 240 | } 241 | 242 | my_bool ParamBuffer::IsUnsigned() { 243 | return _isUnsigned; 244 | } 245 | 246 | my_bool *ParamBuffer::IsTruncated() { 247 | return &_isTruncated; 248 | } 249 | 250 | my_bool *ParamBuffer::Error() { 251 | return &_error; 252 | } 253 | 254 | } 255 | -------------------------------------------------------------------------------- /ParamBuffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "mysql.h" 6 | #include "Binary.h" 7 | #include "Julian.h" 8 | 9 | namespace MySQLWrap { 10 | 11 | class ParamBuffer { 12 | public: 13 | ParamBuffer(const enum_field_types type, my_bool isUnsigned); 14 | ParamBuffer(const std::type_info &info); 15 | ParamBuffer(const std::string &str, size_t maxSize); 16 | ParamBuffer(const std::string &str); 17 | ParamBuffer(const short int i); 18 | ParamBuffer(const unsigned short int i); 19 | ParamBuffer(const int i); 20 | ParamBuffer(const unsigned int i); 21 | ParamBuffer(const Julian &t); 22 | ParamBuffer(const Binary &data); 23 | ParamBuffer(const char ch); 24 | ParamBuffer(const unsigned char ch); 25 | ParamBuffer(const float f); 26 | ParamBuffer(const double d); 27 | 28 | ParamBuffer(const ParamBuffer ©); 29 | ~ParamBuffer(); 30 | 31 | void ResizeBlob(size_t newSize); 32 | 33 | void *Buffer() const; 34 | enum_field_types BufferType() const; 35 | size_t BufferSize() const; 36 | unsigned long *BufferLength(); 37 | my_bool *IsNull(); 38 | my_bool IsUnsigned(); 39 | my_bool *IsTruncated(); 40 | my_bool *Error(); 41 | 42 | private: 43 | void *_buffer; 44 | size_t _bufferSize; 45 | unsigned long _bufferLength; 46 | enum_field_types _type; 47 | my_bool _isNull; 48 | my_bool _isUnsigned; 49 | my_bool _isTruncated; 50 | my_bool _error; 51 | }; 52 | 53 | } 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mysqlwrap 2 | ========= 3 | 4 | C++ Wrapper for MySQL C API 5 | 6 | Features: 7 | - Supports both client and embedded database linkage 8 | - Supports both Prepared Statements (preferred) and AdhocStatements. 9 | - Simplified memory management: Database and Statement objects can be used as automatic variables, which release all required memory when program flow exists scope. 10 | - Stongly typed API - select results can only be extracted into variables of the correct size, and signed-ness. 11 | - Nullable types allow for simplified assigning to and retrieval of NULL values in database fields. 12 | - C++ API hides most MYSQL C API idiosyncracies and make database code more compact 13 | - Supports streaming operator (<<) to fill out parameters in a query, and cause an execute or fetch to occur. 14 | - Supports streaming operator (>>) to retrieve columns from the current result row into Nullable variables. 15 | - Converts "C" return values, errno, and errmsg values into exceptions using DatabaseException class. 16 | 17 | 18 | Limitations: 19 | - Embedded database linkage currently causes a Segmentation Fault when executing Prepared Statements that produce a result set. This appears to be a MYSQL bug. 20 | 21 | 22 | Building: 23 | - There is an included Makefile that has only been tested on OS/X Mountain Lion, but that an enterprising young person like yourself should be able to modify pretty easily to run on most varieties of Unix. 24 | - In the Makefile, you can set certain variables to control the type of build. 25 | - EMBEDDED: embedded or blank ## set to embedded to compile against the embedded server 26 | - LIBRARY_TYPE: dynamic, static, or blank ## type of library produced (blank means no library at all) 27 | - LIBRARY_NAME: name ## name of the library produced. Called libname.dylib for dynamic library 28 | -------------------------------------------------------------------------------- /Statement.cpp: -------------------------------------------------------------------------------- 1 | #include "Statement.h" 2 | #include "DatabaseException.h" 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | namespace MySQLWrap { 10 | 11 | Statement::Statement(Database &db, const std::string &sqlStatement) : _db(db) { 12 | _sqlStatement = sqlStatement; 13 | _resultWasStored = false; 14 | _eof = true; 15 | _currentColumn = 0; 16 | 17 | Prepare(); 18 | } 19 | 20 | Statement::Statement(const Statement ©) : _db(copy._db) { 21 | _sqlStatement = copy._sqlStatement; 22 | _resultWasStored = false; 23 | _eof = true; 24 | _currentColumn = 0; 25 | 26 | Prepare(); 27 | } 28 | 29 | void Statement::Prepare() { 30 | if (! _db.IsConnected()) { 31 | throw DatabaseException("Error in Statement::Prepare", 0, "----", "Database is not connected"); 32 | } 33 | 34 | _numberAffectedRows = 0; 35 | 36 | if ((_stmt = mysql_stmt_init(_db._db)) == NULL) { 37 | throw DatabaseException(_db._db, "Error in Statement::Prepare during initialize"); 38 | } 39 | 40 | if (mysql_stmt_prepare(_stmt, _sqlStatement.c_str(), _sqlStatement.length()) != 0) { 41 | mysql_stmt_close(_stmt); 42 | throw DatabaseException(_stmt, "Error in Statement::Prepare during prepare"); 43 | } 44 | 45 | _bind = NULL; 46 | unsigned long parameterCount = ParameterCount(); 47 | if (parameterCount > 0) { 48 | _bind = (MYSQL_BIND *) malloc(sizeof(MYSQL_BIND) * parameterCount); 49 | memset(_bind, 0, sizeof(MYSQL_BIND) * parameterCount); 50 | } 51 | 52 | _numberResultColumns = 0; 53 | _hasBlobField = false; 54 | 55 | MYSQL_RES *metaData; 56 | if ((metaData = mysql_stmt_result_metadata(_stmt)) == NULL) { 57 | return; 58 | } 59 | 60 | _resultBind = NULL; 61 | _numberResultColumns = mysql_num_fields(metaData); 62 | if (_numberResultColumns > 0) { 63 | _resultBind = (MYSQL_BIND *) malloc(sizeof(MYSQL_BIND) * _numberResultColumns); 64 | memset(_resultBind, 0, sizeof(MYSQL_BIND) * _numberResultColumns); 65 | } 66 | 67 | int fieldPos = 0; 68 | MYSQL_FIELD *field; 69 | while ((field = mysql_fetch_field(metaData)) != NULL) { 70 | ParamBuffer *buffer = NULL; 71 | 72 | if ((field->type == MYSQL_TYPE_VAR_STRING) || 73 | (field->type == MYSQL_TYPE_STRING) || 74 | (field->type == MYSQL_TYPE_DECIMAL) || 75 | (field->type == MYSQL_TYPE_BIT) || 76 | (field->type == MYSQL_TYPE_VARCHAR)) { 77 | std::string str; 78 | buffer = new ParamBuffer(str, field->length); 79 | _resultBind[fieldPos].buffer_type = field->type; 80 | _resultBind[fieldPos].buffer = buffer->Buffer(); 81 | _resultBind[fieldPos].buffer_length = buffer->BufferSize(); 82 | _resultBind[fieldPos].length = buffer->BufferLength(); 83 | _resultBind[fieldPos].is_null = buffer->IsNull(); 84 | _resultBind[fieldPos].error = buffer->Error(); 85 | } 86 | else if (field->type == MYSQL_TYPE_TINY) { 87 | if ((field->flags & UNSIGNED_FLAG) != 0) { 88 | buffer = new ParamBuffer((const unsigned char) 0); 89 | } else { 90 | buffer = new ParamBuffer((const char) 0); 91 | } 92 | _resultBind[fieldPos].buffer_type = MYSQL_TYPE_TINY; 93 | _resultBind[fieldPos].buffer = buffer->Buffer(); 94 | _resultBind[fieldPos].buffer_length = buffer->BufferSize(); 95 | _resultBind[fieldPos].length = buffer->BufferLength(); 96 | _resultBind[fieldPos].is_null = buffer->IsNull(); 97 | _resultBind[fieldPos].error = buffer->Error(); 98 | _resultBind[fieldPos].is_unsigned = buffer->IsUnsigned(); 99 | } 100 | else if ((field->type == MYSQL_TYPE_SHORT) || 101 | (field->type == MYSQL_TYPE_YEAR)) { 102 | if ((field->flags & UNSIGNED_FLAG) != 0) { 103 | buffer = new ParamBuffer((const unsigned short int) 0); 104 | } else { 105 | buffer = new ParamBuffer((const short int) 0); 106 | } 107 | _resultBind[fieldPos].buffer_type = MYSQL_TYPE_SHORT; 108 | _resultBind[fieldPos].buffer = buffer->Buffer(); 109 | _resultBind[fieldPos].buffer_length = buffer->BufferSize(); 110 | _resultBind[fieldPos].length = buffer->BufferLength(); 111 | _resultBind[fieldPos].is_null = buffer->IsNull(); 112 | _resultBind[fieldPos].error = buffer->Error(); 113 | _resultBind[fieldPos].is_unsigned = buffer->IsUnsigned(); 114 | } 115 | else if ((field->type == MYSQL_TYPE_LONG) || 116 | (field->type == MYSQL_TYPE_INT24)) { 117 | if ((field->flags & UNSIGNED_FLAG) != 0) { 118 | buffer = new ParamBuffer((const unsigned int) 0); 119 | } else { 120 | buffer = new ParamBuffer((const int) 0); 121 | } 122 | _resultBind[fieldPos].buffer_type = field->type; 123 | _resultBind[fieldPos].buffer = buffer->Buffer(); 124 | _resultBind[fieldPos].buffer_length = buffer->BufferSize(); 125 | _resultBind[fieldPos].length = buffer->BufferLength(); 126 | _resultBind[fieldPos].is_null = buffer->IsNull(); 127 | _resultBind[fieldPos].error = buffer->Error(); 128 | _resultBind[fieldPos].is_unsigned = buffer->IsUnsigned(); 129 | } 130 | else if (field->type == MYSQL_TYPE_FLOAT) { 131 | _resultBind[fieldPos].buffer_type = field->type; 132 | _resultBind[fieldPos].buffer = buffer->Buffer(); 133 | _resultBind[fieldPos].buffer_length = buffer->BufferSize(); 134 | _resultBind[fieldPos].length = buffer->BufferLength(); 135 | _resultBind[fieldPos].is_null = buffer->IsNull(); 136 | _resultBind[fieldPos].error = buffer->Error(); 137 | _resultBind[fieldPos].is_unsigned = buffer->IsUnsigned(); 138 | } 139 | else if (field->type == MYSQL_TYPE_DOUBLE) { 140 | _resultBind[fieldPos].buffer_type = field->type; 141 | _resultBind[fieldPos].buffer = buffer->Buffer(); 142 | _resultBind[fieldPos].buffer_length = buffer->BufferSize(); 143 | _resultBind[fieldPos].length = buffer->BufferLength(); 144 | _resultBind[fieldPos].is_null = buffer->IsNull(); 145 | _resultBind[fieldPos].error = buffer->Error(); 146 | _resultBind[fieldPos].is_unsigned = buffer->IsUnsigned(); 147 | } 148 | else if ((field->type == MYSQL_TYPE_TIMESTAMP) || 149 | (field->type == MYSQL_TYPE_DATE) || 150 | (field->type == MYSQL_TYPE_TIME) || 151 | (field->type == MYSQL_TYPE_DATETIME)) { 152 | Julian time; 153 | buffer = new ParamBuffer(time); 154 | _resultBind[fieldPos].buffer_type = field->type; 155 | _resultBind[fieldPos].buffer = buffer->Buffer(); 156 | _resultBind[fieldPos].buffer_length = buffer->BufferSize(); 157 | _resultBind[fieldPos].length = buffer->BufferLength(); 158 | _resultBind[fieldPos].is_null = buffer->IsNull(); 159 | _resultBind[fieldPos].error = buffer->Error(); 160 | } 161 | else if ((field->type == MYSQL_TYPE_BLOB) || 162 | (field->type == MYSQL_TYPE_TINY_BLOB) || 163 | (field->type == MYSQL_TYPE_MEDIUM_BLOB) || 164 | (field->type == MYSQL_TYPE_LONG_BLOB)) { 165 | _hasBlobField = true; 166 | Binary data(field->length); 167 | buffer = new ParamBuffer(data); 168 | _resultBind[fieldPos].buffer_type = field->type; 169 | _resultBind[fieldPos].buffer = buffer->Buffer(); 170 | _resultBind[fieldPos].buffer_length = buffer->BufferSize(); 171 | _resultBind[fieldPos].length = buffer->BufferLength(); 172 | _resultBind[fieldPos].is_null = buffer->IsNull(); 173 | _resultBind[fieldPos].error = buffer->Error(); 174 | } 175 | 176 | if (buffer != NULL) { 177 | _resultParams.push_back(buffer); 178 | } 179 | fieldPos++; 180 | } 181 | 182 | if (_resultParams.size() != _numberResultColumns) { 183 | mysql_free_result(metaData); 184 | mysql_stmt_close(_stmt); 185 | throw DatabaseException("Error in Statement::Prepare", 0, "----", "was not able to bind all parameters"); 186 | } 187 | 188 | if (_hasBlobField) { 189 | my_bool setMax = 1; 190 | mysql_stmt_attr_set(_stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &setMax); 191 | } 192 | 193 | if (_numberResultColumns > 0) { 194 | if (mysql_stmt_bind_result(_stmt, _resultBind) != 0) { 195 | mysql_free_result(metaData); 196 | mysql_stmt_close(_stmt); 197 | throw DatabaseException(_stmt, "Error in Statement::Prepare while binding results"); 198 | } 199 | } 200 | 201 | mysql_free_result(metaData); 202 | } 203 | 204 | Statement::~Statement() { 205 | if (_resultWasStored) { 206 | mysql_stmt_free_result(_stmt); 207 | } 208 | mysql_stmt_close(_stmt); 209 | ClearParameters(); 210 | ClearResults(); 211 | } 212 | 213 | unsigned long Statement::RemainingParameters() { 214 | return ParameterCount() - _params.size(); 215 | } 216 | 217 | unsigned long Statement::ParameterCount() { 218 | return mysql_stmt_param_count(_stmt); 219 | } 220 | 221 | void Statement::AssignNextParameter(const Nullable ¶m) { 222 | AssignNextParameterTemplate(param); 223 | } 224 | 225 | void Statement::AssignNextParameter(const Nullable ¶m) { 226 | AssignNextParameterTemplate(param); 227 | } 228 | 229 | void Statement::AssignNextParameter(const Nullable ¶m) { 230 | AssignNextParameterTemplate(param); 231 | } 232 | 233 | void Statement::AssignNextParameter(const Nullable ¶m) { 234 | AssignNextParameterTemplate(param); 235 | } 236 | 237 | void Statement::AssignNextParameter(const Nullable ¶m) { 238 | AssignNextParameterTemplate(param); 239 | } 240 | 241 | void Statement::AssignNextParameter(const Nullable ¶m) { 242 | AssignNextParameterTemplate(param); 243 | } 244 | 245 | void Statement::AssignNextParameter(const Nullable ¶m) { 246 | AssignNextParameterTemplate(param); 247 | } 248 | 249 | void Statement::AssignNextParameter(const Nullable ¶m) { 250 | AssignNextParameterTemplate(param); 251 | } 252 | 253 | void Statement::AssignNextParameter(const Nullable ¶m) { 254 | AssignNextParameterTemplate(param); 255 | } 256 | 257 | void Statement::AssignNextParameter(const Nullable ¶m) { 258 | AssignNextParameterTemplate(param); 259 | } 260 | 261 | void Statement::AssignNextParameter(const Nullable ¶m) { 262 | AssignNextParameterTemplate(param); 263 | } 264 | 265 | void Statement::AssignNextParameter(ParamBuffer *buffer) { 266 | if (buffer == NULL) { 267 | throw DatabaseException("Error in Statement::AssignNextParameter", 0, "----", "Buffer cannot be null"); 268 | } 269 | 270 | unsigned int pos = _params.size(); 271 | if (pos >= ParameterCount()) { 272 | delete buffer; 273 | throw DatabaseException("Error in Statement::AssignNextParameter", 0, "----", "Have already assigned all possible input parameters"); 274 | } 275 | 276 | _params.push_back(buffer); 277 | 278 | _bind[pos].buffer_type = buffer->BufferType(); 279 | _bind[pos].buffer = buffer->Buffer(); 280 | _bind[pos].buffer_length = buffer->BufferSize(); 281 | _bind[pos].is_null = buffer->IsNull(); 282 | _bind[pos].length = buffer->BufferLength(); 283 | _bind[pos].is_unsigned = buffer->IsUnsigned(); 284 | } 285 | 286 | Nullable Statement::GetLongDataInRow(unsigned int column) { 287 | Nullable result; 288 | GetDataInRow(column, result); 289 | return result; 290 | } 291 | 292 | Nullable Statement::GetULongDataInRow(unsigned int column) { 293 | Nullable result; 294 | GetDataInRow(column, result); 295 | return result; 296 | } 297 | 298 | void Statement::Execute() { 299 | if (! _db.IsConnected()) { 300 | throw DatabaseException("Error in Statement::Execute", 0, "----", "Database is not connected"); 301 | } 302 | 303 | _numberAffectedRows = 0; 304 | _eof = true; 305 | _currentColumn = 0; 306 | 307 | if (ParameterCount() != _params.size()) { 308 | throw DatabaseException("Error in Statement::Execute", 0, "----", "Have not yet assigned all parameters"); 309 | } 310 | 311 | if (mysql_stmt_bind_param(_stmt, _bind) != 0) { 312 | throw DatabaseException(_stmt, "Error in Statement::Execute while binding parameters"); 313 | } 314 | 315 | if (mysql_stmt_execute(_stmt) != 0) { 316 | throw DatabaseException(_stmt, "Error in Statement::Execute while executing statement"); 317 | } 318 | 319 | if (_numberResultColumns > 0) { 320 | if (mysql_stmt_store_result(_stmt) != 0) { 321 | throw DatabaseException(_stmt, "Error in Statement::Execute while storing results"); 322 | } 323 | _eof = false; 324 | } 325 | else { 326 | _numberAffectedRows = mysql_stmt_affected_rows(_stmt); 327 | } 328 | 329 | _resultWasStored = true; 330 | 331 | if (_hasBlobField && _resultBind != NULL) { 332 | MYSQL_RES *res = mysql_stmt_result_metadata(_stmt); 333 | if (res != NULL) { 334 | for (unsigned int i = 0; i < _resultParams.size(); i++) { 335 | MYSQL_FIELD *column = mysql_fetch_field_direct(res, i); 336 | if (_resultBind[i].buffer_type == MYSQL_TYPE_BLOB) { 337 | _resultParams[i]->ResizeBlob(column->max_length); 338 | _resultBind[i].buffer = _resultParams[i]->Buffer(); 339 | _resultBind[i].buffer_length = _resultParams[i]->BufferSize(); 340 | _resultBind[i].length = _resultParams[i]->BufferLength(); 341 | } 342 | } 343 | mysql_free_result(res); 344 | } 345 | } 346 | } 347 | 348 | void Statement::ResetParameters() { 349 | while (! _params.empty()) { 350 | ParamBuffer *buf = _params.back(); 351 | _params.pop_back(); 352 | delete buf; 353 | } 354 | 355 | mysql_stmt_reset(_stmt); 356 | mysql_stmt_free_result(_stmt); 357 | _eof = true; 358 | } 359 | 360 | void Statement::ClearParameters() { 361 | ResetParameters(); 362 | if (_bind != NULL) { 363 | free(_bind); 364 | } 365 | } 366 | 367 | void Statement::ClearResults() { 368 | _eof = true; 369 | while (! _resultParams.empty()) { 370 | ParamBuffer *buf = _resultParams.back(); 371 | _resultParams.pop_back(); 372 | delete buf; 373 | } 374 | 375 | if ((_numberResultColumns > 0) && (_resultBind != NULL)) { 376 | free(_resultBind); 377 | } 378 | } 379 | 380 | bool Statement::Eof() { 381 | return _eof; 382 | } 383 | 384 | bool Statement::FetchNextRow() { 385 | _currentColumn = 0; 386 | bool ret = true; 387 | int result = mysql_stmt_fetch(_stmt); 388 | if (result == 1) { 389 | throw DatabaseException(_stmt, "Error in Statement::FetchNewRow while fetching"); 390 | } 391 | else if (result == MYSQL_NO_DATA) { 392 | ret = false; 393 | } 394 | else if (result == MYSQL_DATA_TRUNCATED) { 395 | throw DatabaseException("Error in Statement::FetchNewRow while fetching", 0, "----", "Data would have been truncated."); 396 | } 397 | _eof = !ret; 398 | return ret; 399 | } 400 | 401 | void Statement::GetDataInRow(unsigned int column, Nullable &result) { 402 | if (column >= _resultParams.size()) { 403 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column out of range"); 404 | } 405 | 406 | if (_resultBind[column].buffer_type != MYSQL_TYPE_TINY) { 407 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column not of correct type"); 408 | } 409 | 410 | if (_resultBind[column].is_unsigned) { 411 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column is an unsigned data type"); 412 | } 413 | 414 | if (! (*(_resultParams[column]->IsNull()))) { 415 | result = *((char *) _resultParams[column]->Buffer()); 416 | } else { 417 | result.ClearValue(); 418 | } 419 | } 420 | 421 | void Statement::GetDataInRow(unsigned int column, Nullable &result) { 422 | if (column >= _resultParams.size()) { 423 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column out of range"); 424 | } 425 | 426 | if (_resultBind[column].buffer_type != MYSQL_TYPE_TINY) { 427 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column not of correct type"); 428 | } 429 | 430 | if (! _resultBind[column].is_unsigned) { 431 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column is not an unsigned data type"); 432 | } 433 | 434 | if (! (*(_resultParams[column]->IsNull()))) { 435 | result = *((char *) _resultParams[column]->Buffer()); 436 | } else { 437 | result.ClearValue(); 438 | } 439 | } 440 | 441 | Nullable Statement::GetTinyDataInRow(unsigned int column) { 442 | Nullable result; 443 | GetDataInRow(column, result); 444 | return result; 445 | } 446 | 447 | Nullable Statement::GetUTinyDataInRow(unsigned int column) { 448 | Nullable result; 449 | GetDataInRow(column, result); 450 | return result; 451 | } 452 | 453 | void Statement::GetDataInRow(unsigned int column, Nullable &result) { 454 | if (column >= _resultParams.size()) { 455 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column out of range"); 456 | } 457 | 458 | if (_resultBind[column].buffer_type != MYSQL_TYPE_LONG) { 459 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column not of correct type"); 460 | } 461 | 462 | if (_resultBind[column].is_unsigned) { 463 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column is an unsigned data type"); 464 | } 465 | 466 | if (! (*(_resultParams[column]->IsNull()))) { 467 | result = *((int *) _resultParams[column]->Buffer()); 468 | } else { 469 | result.ClearValue(); 470 | } 471 | } 472 | 473 | void Statement::GetDataInRow(unsigned int column, Nullable &result) { 474 | if (column >= _resultParams.size()) { 475 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column out of range"); 476 | } 477 | 478 | if (_resultBind[column].buffer_type != MYSQL_TYPE_LONG) { 479 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column not of correct type"); 480 | } 481 | 482 | if (! _resultBind[column].is_unsigned) { 483 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column is not an unsigned data type"); 484 | } 485 | 486 | if (! (*(_resultParams[column]->IsNull()))) { 487 | result = *((int *) _resultParams[column]->Buffer()); 488 | } else { 489 | result.ClearValue(); 490 | } 491 | } 492 | 493 | void Statement::GetDataInRow(unsigned int column, Nullable &result) { 494 | if (column >= _resultParams.size()) { 495 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column out of range"); 496 | } 497 | 498 | if ((_resultBind[column].buffer_type != MYSQL_TYPE_SHORT) && 499 | (_resultBind[column].buffer_type != MYSQL_TYPE_YEAR)) { 500 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column not of correct type"); 501 | } 502 | 503 | if (_resultBind[column].is_unsigned) { 504 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column is an unsigned data type"); 505 | } 506 | 507 | if (! (*(_resultParams[column]->IsNull()))) { 508 | result = *((short int *) _resultParams[column]->Buffer()); 509 | } else { 510 | result.ClearValue(); 511 | } 512 | } 513 | 514 | void Statement::GetDataInRow(unsigned int column, Nullable &result) { 515 | if (column >= _resultParams.size()) { 516 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column out of range"); 517 | } 518 | 519 | if ((_resultBind[column].buffer_type != MYSQL_TYPE_SHORT) && 520 | (_resultBind[column].buffer_type != MYSQL_TYPE_YEAR)) { 521 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column not of correct type"); 522 | } 523 | 524 | if (! _resultBind[column].is_unsigned) { 525 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column is not an unsigned data type"); 526 | } 527 | 528 | if (! (*(_resultParams[column]->IsNull()))) { 529 | result = *((short int *) _resultParams[column]->Buffer()); 530 | } else { 531 | result.ClearValue(); 532 | } 533 | } 534 | 535 | Nullable Statement::GetShortDataInRow(unsigned int column) { 536 | Nullable result; 537 | GetDataInRow(column, result); 538 | return result; 539 | } 540 | 541 | Nullable Statement::GetUShortDataInRow(unsigned int column) { 542 | Nullable result; 543 | GetDataInRow(column, result); 544 | return result; 545 | } 546 | 547 | void Statement::GetDataInRow(unsigned int column, Nullable &result) { 548 | if (column >= _resultParams.size()) { 549 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column out of range"); 550 | } 551 | 552 | if ((_resultBind[column].buffer_type != MYSQL_TYPE_VAR_STRING) && 553 | (_resultBind[column].buffer_type != MYSQL_TYPE_STRING) && 554 | (_resultBind[column].buffer_type != MYSQL_TYPE_VARCHAR) && 555 | (_resultBind[column].buffer_type != MYSQL_TYPE_DECIMAL) && 556 | (_resultBind[column].buffer_type != MYSQL_TYPE_BIT)) { 557 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column not of correct type"); 558 | } 559 | 560 | if (! (*(_resultParams[column]->IsNull()))) { 561 | result = std::string((const char *) _resultParams[column]->Buffer(), *(_resultParams[column]->BufferLength())); 562 | } else { 563 | result.ClearValue(); 564 | } 565 | } 566 | 567 | Nullable Statement::GetStringDataInRow(unsigned int column) { 568 | Nullable result; 569 | GetDataInRow(column, result); 570 | return result; 571 | } 572 | 573 | void Statement::GetDataInRow(unsigned int column, Nullable &result) { 574 | if (column >= _resultParams.size()) { 575 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column out of range"); 576 | } 577 | 578 | if (_resultBind[column].buffer_type != MYSQL_TYPE_DOUBLE) { 579 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column not of correct type"); 580 | } 581 | 582 | if (! (*(_resultParams[column]->IsNull()))) { 583 | result = *((double *) _resultParams[column]->Buffer()); 584 | } else { 585 | result.ClearValue(); 586 | } 587 | } 588 | 589 | Nullable Statement::GetDoubleDataInRow(unsigned int column) { 590 | Nullable result; 591 | GetDataInRow(column, result); 592 | return result; 593 | } 594 | 595 | void Statement::GetDataInRow(unsigned int column, Nullable &result) { 596 | if (column >= _resultParams.size()) { 597 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column out of range"); 598 | } 599 | 600 | if (_resultBind[column].buffer_type != MYSQL_TYPE_FLOAT) { 601 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column not of correct type"); 602 | } 603 | 604 | if (! (*(_resultParams[column]->IsNull()))) { 605 | result = *((float *) _resultParams[column]->Buffer()); 606 | } else { 607 | result.ClearValue(); 608 | } 609 | } 610 | 611 | Nullable Statement::GetFloatDataInRow(unsigned int column) { 612 | Nullable result; 613 | GetDataInRow(column, result); 614 | return result; 615 | } 616 | 617 | void Statement::GetDataInRow(unsigned int column, Nullable &result) { 618 | if (column >= _resultParams.size()) { 619 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column out of range"); 620 | } 621 | 622 | if ((_resultBind[column].buffer_type != MYSQL_TYPE_TIMESTAMP) && 623 | (_resultBind[column].buffer_type != MYSQL_TYPE_DATE) && 624 | (_resultBind[column].buffer_type != MYSQL_TYPE_TIME) && 625 | (_resultBind[column].buffer_type != MYSQL_TYPE_DATETIME)) { 626 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column not of correct type"); 627 | } 628 | 629 | MYSQL_TIME time; 630 | if (! (*(_resultParams[column]->IsNull()))) { 631 | time = *((MYSQL_TIME *) _resultParams[column]->Buffer()); 632 | GregorianBreakdown gb(time, 0); 633 | result = Julian(gb); 634 | } else { 635 | result.ClearValue(); 636 | } 637 | } 638 | 639 | Nullable Statement::GetTimeDataInRow(unsigned int column) { 640 | Nullable result; 641 | GetDataInRow(column, result); 642 | return result; 643 | } 644 | 645 | void Statement::GetDataInRow(unsigned int column, Nullable &result) { 646 | if (column >= _resultParams.size()) { 647 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column out of range"); 648 | } 649 | 650 | if (! (*(_resultParams[column]->IsNull()))) { 651 | if (_resultBind[column].buffer_type == MYSQL_TYPE_TINY_BLOB) { 652 | if (*(_resultParams[column]->BufferLength()) >= 256) { 653 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "data too large for Blob type"); 654 | } 655 | } else if (_resultBind[column].buffer_type == MYSQL_TYPE_BLOB) { 656 | if (*(_resultParams[column]->BufferLength()) >= 64 * 1024) { 657 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "data too large for Blob type"); 658 | } 659 | } else if (_resultBind[column].buffer_type == MYSQL_TYPE_MEDIUM_BLOB) { 660 | if (*(_resultParams[column]->BufferLength()) >= 16 * 1024 * 1024) { 661 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "data too large for Medium Blob type"); 662 | } 663 | } else if (_resultBind[column].buffer_type == MYSQL_TYPE_LONG_BLOB) { 664 | if (*(_resultParams[column]->BufferLength()) >= (size_t) 4 * 1024 * 1024 * 1024) { 665 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "data too large for Large Blob type"); 666 | } 667 | } else { 668 | throw DatabaseException("Error in Statement::GetDataInRow", 0, "----", "column not of correct type (should be Blob)"); 669 | } 670 | 671 | if (mysql_stmt_fetch_column(_stmt, &(_resultBind[column]), column, 0) != 0) { 672 | throw DatabaseException(_stmt, "Error in GetDataInRow"); 673 | } 674 | 675 | Binary fromdb; 676 | fromdb.AssignDataToBuffer((unsigned char *)_resultParams[column]->Buffer(), *(_resultParams[column]->BufferLength())); 677 | result = fromdb; 678 | } else { 679 | result.ClearValue(); 680 | } 681 | } 682 | 683 | Nullable Statement::GetBinaryDataInRow(unsigned int column) { 684 | Nullable result; 685 | GetDataInRow(column, result); 686 | return result; 687 | } 688 | 689 | unsigned long long Statement::NumberOfAffectedRows() { 690 | return _numberAffectedRows; 691 | } 692 | 693 | unsigned long long Statement::NumberOfReturnedRows() { 694 | return mysql_stmt_num_rows(_stmt); 695 | } 696 | 697 | Statement::operator bool() { 698 | return ! Eof(); 699 | } 700 | 701 | unsigned int Statement::GetNextDataColumn() { 702 | unsigned int result = _currentColumn; 703 | _currentColumn++; 704 | return result; 705 | } 706 | 707 | } 708 | -------------------------------------------------------------------------------- /Statement.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Database.h" 5 | #include "ParamBuffer.h" 6 | #include "Nullable.h" 7 | #include "IStatement.h" 8 | #include 9 | 10 | namespace MySQLWrap { 11 | 12 | class Statement : public IStatement { 13 | public: 14 | Statement(Database &db, const std::string &sqlStatement); 15 | Statement(const Statement &); 16 | virtual ~Statement(); 17 | 18 | virtual unsigned long ParameterCount(); 19 | virtual unsigned long RemainingParameters(); 20 | virtual void ResetParameters(); 21 | virtual void Execute(); 22 | virtual bool FetchNextRow(); 23 | virtual bool Eof(); 24 | virtual unsigned long long NumberOfAffectedRows(); 25 | virtual operator bool(); 26 | virtual unsigned long long NumberOfReturnedRows(); 27 | 28 | virtual Nullable GetTinyDataInRow(unsigned int column); 29 | virtual Nullable GetUTinyDataInRow(unsigned int column); 30 | virtual Nullable GetShortDataInRow(unsigned int column); 31 | virtual Nullable GetUShortDataInRow(unsigned int column); 32 | virtual Nullable GetLongDataInRow(unsigned int column); 33 | virtual Nullable GetULongDataInRow(unsigned int column); 34 | virtual Nullable GetTimeDataInRow(unsigned int column); 35 | virtual Nullable GetStringDataInRow(unsigned int column); 36 | virtual Nullable GetBinaryDataInRow(unsigned int column); 37 | virtual Nullable GetFloatDataInRow(unsigned int column); 38 | virtual Nullable GetDoubleDataInRow(unsigned int column); 39 | 40 | virtual void AssignNextParameter(const Nullable &data); 41 | virtual void AssignNextParameter(const Nullable &data); 42 | virtual void AssignNextParameter(const Nullable &data); 43 | virtual void AssignNextParameter(const Nullable &data); 44 | virtual void AssignNextParameter(const Nullable &data); 45 | virtual void AssignNextParameter(const Nullable &data); 46 | virtual void AssignNextParameter(const Nullable &data); 47 | virtual void AssignNextParameter(const Nullable &data); 48 | virtual void AssignNextParameter(const Nullable &data); 49 | virtual void AssignNextParameter(const Nullable &data); 50 | virtual void AssignNextParameter(const Nullable &data); 51 | 52 | virtual void GetDataInRow(unsigned int column, Nullable &data); 53 | virtual void GetDataInRow(unsigned int column, Nullable &data); 54 | virtual void GetDataInRow(unsigned int column, Nullable &data); 55 | virtual void GetDataInRow(unsigned int column, Nullable &data); 56 | virtual void GetDataInRow(unsigned int column, Nullable &data); 57 | virtual void GetDataInRow(unsigned int column, Nullable &data); 58 | virtual void GetDataInRow(unsigned int column, Nullable &data); 59 | virtual void GetDataInRow(unsigned int column, Nullable &data); 60 | virtual void GetDataInRow(unsigned int column, Nullable &data); 61 | virtual void GetDataInRow(unsigned int column, Nullable &data); 62 | virtual void GetDataInRow(unsigned int column, Nullable &data); 63 | 64 | protected: 65 | virtual unsigned int GetNextDataColumn(); 66 | 67 | template 68 | void AssignNextParameterTemplate(const Nullable ¶m) { 69 | if (! param.HasValue()) { 70 | AssignNextParameter(new ParamBuffer(typeid(Binary))); 71 | } else { 72 | AssignNextParameter(new ParamBuffer(param.const_deref())); 73 | } 74 | } 75 | 76 | private: 77 | void AssignNextParameter(ParamBuffer *buffer); 78 | void Prepare(); 79 | void ClearParameters(); 80 | void ClearResults(); 81 | 82 | unsigned int _numberResultColumns; 83 | unsigned int _currentColumn; 84 | bool _hasBlobField; 85 | my_ulonglong _numberAffectedRows; 86 | 87 | Database &_db; 88 | MYSQL_STMT *_stmt; 89 | MYSQL_BIND *_bind; 90 | MYSQL_BIND *_resultBind; 91 | 92 | std::string _sqlStatement; 93 | bool _resultWasStored; 94 | bool _eof; 95 | 96 | std::vector _params; 97 | std::vector _resultParams; 98 | }; 99 | 100 | } 101 | -------------------------------------------------------------------------------- /TestAdhocParameter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "UTFail.h" 6 | #include "TestAdhocParameter.h" 7 | #include "AdhocParameter.h" 8 | 9 | using namespace std; 10 | 11 | TestAdhocParameter::TestAdhocParameter() {} 12 | 13 | void TestAdhocParameter::Test1() { 14 | cout << __PRETTY_FUNCTION__ << endl; 15 | AdhocParameter p; 16 | UTASSERT(p.IsNull()); 17 | p.SetData((unsigned char) 220); 18 | UTASSERT(! p.IsNull()); 19 | 20 | std::string result = p.Get(); 21 | UTASSERT(result.size() == 1); 22 | unsigned char res0 = (unsigned char) result[0]; 23 | UTASSERT(res0 == (unsigned char) 220); 24 | } 25 | 26 | void TestAdhocParameter::Test2() { 27 | cout << __PRETTY_FUNCTION__ << endl; 28 | AdhocParameter p; 29 | UTASSERT(p.IsNull()); 30 | p.SetData((char) 'R'); 31 | UTASSERT(! p.IsNull()); 32 | 33 | std::string result = p.Get(); 34 | UTASSERT(result.size() == 1); 35 | char res0 = (char) result[0]; 36 | UTASSERT(res0 == (char) 'R'); 37 | } 38 | 39 | void TestAdhocParameter::Test3() { 40 | cout << __PRETTY_FUNCTION__ << endl; 41 | AdhocParameter p; 42 | UTASSERT(p.IsNull()); 43 | p.SetData((unsigned short) 16000); 44 | UTASSERT(! p.IsNull()); 45 | 46 | std::string result = p.Get(); 47 | UTASSERT(result.size() == 5); 48 | UTASSERT(strcmp(result.c_str(), "16000") == 0); 49 | } 50 | 51 | void TestAdhocParameter::Test4() { 52 | cout << __PRETTY_FUNCTION__ << endl; 53 | 54 | AdhocParameter p; 55 | UTASSERT(p.IsNull()); 56 | p.SetData((short) 8100); 57 | UTASSERT(! p.IsNull()); 58 | 59 | std::string result = p.Get(); 60 | UTASSERT(result.size() == 4); 61 | UTASSERT(strcmp(result.c_str(), "8100") == 0); 62 | } 63 | 64 | void TestAdhocParameter::Test5() { 65 | cout << __PRETTY_FUNCTION__ << endl; 66 | 67 | AdhocParameter p; 68 | UTASSERT(p.IsNull()); 69 | p.SetData((unsigned int) 3000000000); 70 | UTASSERT(! p.IsNull()); 71 | 72 | std::string result = p.Get(); 73 | UTASSERT(result.size() == 10); 74 | UTASSERT(strcmp(result.c_str(), "3000000000") == 0); 75 | } 76 | 77 | 78 | void TestAdhocParameter::Test6() { 79 | cout << __PRETTY_FUNCTION__ << endl; 80 | 81 | AdhocParameter p; 82 | UTASSERT(p.IsNull()); 83 | p.SetData((int) 1000000000); 84 | UTASSERT(! p.IsNull()); 85 | 86 | std::string result = p.Get(); 87 | UTASSERT(result.size() == 10); 88 | UTASSERT(strcmp(result.c_str(), "1000000000") == 0); 89 | } 90 | 91 | void TestAdhocParameter::Test7() { 92 | cout << __PRETTY_FUNCTION__ << endl; 93 | 94 | AdhocParameter p; 95 | UTASSERT(p.IsNull()); 96 | p.SetData((float) 3.14f); 97 | UTASSERT(! p.IsNull()); 98 | 99 | std::string result = p.Get(); 100 | cout << result << endl; 101 | UTASSERT(result.size() == 4); 102 | UTASSERT(strcmp(result.c_str(), "3.14") == 0); 103 | } 104 | 105 | int TestAdhocParameter::RunSpecificTest(AdhocParameterMemberPointer test) { 106 | int failures = 0; 107 | try { 108 | (this->*test)(); 109 | } catch (UTFail &fail) { 110 | failures++; 111 | cout << fail << endl; 112 | } 113 | return failures; 114 | } 115 | 116 | int TestAdhocParameter::RunTests() { 117 | int failures = 0; 118 | failures += RunSpecificTest(&TestAdhocParameter::Test1); 119 | failures += RunSpecificTest(&TestAdhocParameter::Test2); 120 | failures += RunSpecificTest(&TestAdhocParameter::Test3); 121 | failures += RunSpecificTest(&TestAdhocParameter::Test4); 122 | failures += RunSpecificTest(&TestAdhocParameter::Test5); 123 | failures += RunSpecificTest(&TestAdhocParameter::Test6); 124 | failures += RunSpecificTest(&TestAdhocParameter::Test7); 125 | return failures; 126 | } 127 | -------------------------------------------------------------------------------- /TestAdhocParameter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Nullable.h" 5 | #include "UTFail.h" 6 | 7 | using namespace MySQLWrap; 8 | 9 | class TestAdhocParameter; 10 | typedef void (TestAdhocParameter::*AdhocParameterMemberPointer)(); 11 | 12 | class TestAdhocParameter { 13 | public: 14 | TestAdhocParameter(); 15 | int RunTests(); 16 | private: 17 | void Test1(); 18 | void Test2(); 19 | void Test3(); 20 | void Test4(); 21 | void Test5(); 22 | void Test6(); 23 | void Test7(); 24 | 25 | int RunSpecificTest(AdhocParameterMemberPointer test); 26 | }; 27 | -------------------------------------------------------------------------------- /TestBinary.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "UTFail.h" 3 | #include "TestBinary.h" 4 | 5 | using namespace std; 6 | 7 | TestBinary::TestBinary() {} 8 | 9 | void TestBinary::Test1() { 10 | cout << __PRETTY_FUNCTION__ << endl; 11 | 12 | Binary b1; 13 | UTASSERT(b1.BufferLength() == 0); 14 | UTASSERT(b1.BufferSize() == 0); 15 | UTASSERT(b1.Buffer() == NULL); 16 | 17 | unsigned char *buffer = (unsigned char *)malloc(10); 18 | for (unsigned int i = 0; i < 10; i++) { 19 | buffer[i] = i + 1; 20 | } 21 | 22 | Binary b2; 23 | b2.SubsumeBuffer(buffer, 10, 10); 24 | UTASSERT(b2.BufferLength() == 10); 25 | UTASSERT(b2.BufferSize() == 10); 26 | UTASSERT(b2.Buffer() == buffer); 27 | 28 | b1.AssignDataToBuffer(buffer, 10); 29 | UTASSERT(b1.BufferLength() == 10); 30 | UTASSERT(b1.BufferSize() == 10); 31 | UTASSERT(b1.Buffer() != buffer); 32 | } 33 | 34 | void TestBinary::Test2() { 35 | cout << __PRETTY_FUNCTION__ << endl; 36 | 37 | unsigned char *buffer = (unsigned char *)malloc(10); 38 | for (unsigned int i = 0; i < 10; i++) { 39 | buffer[i] = i + 1; 40 | } 41 | 42 | Binary b1(buffer, 10, 10); 43 | 44 | Binary b2(b1); 45 | UTASSERT(b2.BufferLength() == 10); 46 | UTASSERT(b2.BufferSize() == 10); 47 | UTASSERT(b2.Buffer() == buffer); 48 | UTASSERT(b1.BufferLength() == 0); 49 | UTASSERT(b1.BufferSize() == 0); 50 | UTASSERT(b1.Buffer() == NULL); 51 | } 52 | 53 | void TestBinary::Test3() { 54 | cout << __PRETTY_FUNCTION__ << endl; 55 | 56 | unsigned char *buffer = (unsigned char *)malloc(10); 57 | for (unsigned int i = 0; i < 10; i++) { 58 | buffer[i] = i + 1; 59 | } 60 | 61 | Binary b1(buffer, 10, 10); 62 | Binary b2; 63 | b2.AssignDataToBuffer(buffer, 10); 64 | 65 | UTASSERT(b1.Buffer() != b2.Buffer()); 66 | UTASSERT(b1.BufferLength() == 10); 67 | UTASSERT(b2.BufferLength() == 10); 68 | 69 | b2.ResizeBuffer(100); 70 | UTASSERT(b2.BufferLength() == 10); 71 | UTASSERT(b2.BufferSize() == 100); 72 | 73 | for (int i = 0; i < 10; i++) { 74 | UTASSERT(b1.Buffer()[i] == b2.Buffer()[i]); 75 | } 76 | 77 | UTASSERT(b1 == b2); 78 | } 79 | 80 | void TestBinary::Test4() { 81 | cout << __PRETTY_FUNCTION__ << endl; 82 | 83 | Binary b1(128); 84 | UTASSERT(b1.Buffer() != NULL); 85 | UTASSERT(b1.BufferLength() == 0); 86 | UTASSERT(b1.BufferSize() == 128); 87 | 88 | unsigned char *buffer = (unsigned char *)malloc(10); 89 | for (unsigned int i = 0; i < 10; i++) { 90 | buffer[i] = i + 1; 91 | } 92 | 93 | Binary b2(buffer, 10, 10); 94 | 95 | b1.AssignDataToBuffer(buffer, 10); 96 | UTASSERT(b1.BufferLength() == 10); 97 | UTASSERT(b1.BufferSize() == 128); 98 | 99 | //Sizing too small should have no effect 100 | b1.ResizeBuffer(5); 101 | UTASSERT(b1.BufferLength() == 10); 102 | UTASSERT(b1.BufferSize() == 128); 103 | UTASSERT(b1 == b2); 104 | 105 | b1.ResizeBuffer(256); 106 | UTASSERT(b1.BufferSize() == 256) 107 | UTASSERT(b1 == b2); 108 | } 109 | 110 | void TestBinary::Test5() { 111 | cout << __PRETTY_FUNCTION__ << endl; 112 | 113 | unsigned char *buffer = (unsigned char *)malloc(10); 114 | for (unsigned int i = 0; i < 10; i++) { 115 | buffer[i] = i + 1; 116 | } 117 | 118 | Binary b1(buffer, 10, 10); 119 | b1.ClearBuffer(); 120 | 121 | UTASSERT(b1.Buffer() == NULL); 122 | UTASSERT(b1.BufferLength() == 0); 123 | UTASSERT(b1.BufferSize() == 0); 124 | } 125 | 126 | 127 | void TestBinary::Test6() { 128 | cout << __PRETTY_FUNCTION__ << endl; 129 | } 130 | 131 | void TestBinary::Test7() { 132 | cout << __PRETTY_FUNCTION__ << endl; 133 | } 134 | 135 | int TestBinary::RunSpecificTest(BinaryMemberPointer test) { 136 | int failures = 0; 137 | try { 138 | (this->*test)(); 139 | } catch (UTFail &fail) { 140 | failures++; 141 | cout << fail << endl; 142 | } 143 | return failures; 144 | } 145 | 146 | int TestBinary::RunTests() { 147 | int failures = 0; 148 | failures += RunSpecificTest(&TestBinary::Test1); 149 | failures += RunSpecificTest(&TestBinary::Test2); 150 | failures += RunSpecificTest(&TestBinary::Test3); 151 | failures += RunSpecificTest(&TestBinary::Test4); 152 | failures += RunSpecificTest(&TestBinary::Test5); 153 | failures += RunSpecificTest(&TestBinary::Test6); 154 | failures += RunSpecificTest(&TestBinary::Test7); 155 | return failures; 156 | } 157 | -------------------------------------------------------------------------------- /TestBinary.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Binary.h" 5 | #include "UTFail.h" 6 | 7 | using namespace std; 8 | using namespace MySQLWrap; 9 | 10 | class TestBinary; 11 | typedef void (TestBinary::*BinaryMemberPointer)(); 12 | 13 | class TestBinary { 14 | public: 15 | TestBinary(); 16 | int RunTests(); 17 | private: 18 | void Test1(); 19 | void Test2(); 20 | void Test3(); 21 | void Test4(); 22 | void Test5(); 23 | void Test6(); 24 | void Test7(); 25 | 26 | int RunSpecificTest(BinaryMemberPointer test); 27 | }; 28 | -------------------------------------------------------------------------------- /TestDatabase.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "UTFail.h" 6 | #include "TestDatabase.h" 7 | #include "Database.h" 8 | #include "DatabaseException.h" 9 | #include "Statement.h" 10 | #include "AdhocStatement.h" 11 | 12 | #include 13 | #include 14 | 15 | using namespace std; 16 | 17 | TestDatabase::TestDatabase() {} 18 | 19 | void TestDatabase::Test1() { 20 | cout << __PRETTY_FUNCTION__ << endl; 21 | 22 | char line[512]; 23 | bool wasCaught = false; 24 | try { 25 | Database db("localhost", "root", "", "baddb", 0, NULL, 0); 26 | db.Connect(); 27 | } catch (const DatabaseException &de) { 28 | std::stringstream ss; 29 | ss << de << endl; 30 | ss.getline(line, 512); 31 | wasCaught = true; 32 | } 33 | 34 | UTASSERT(wasCaught); 35 | UTASSERT(strcmp(line, "Error in Database::Connect ERROR 1049(42000) Unknown database 'baddb'") == 0); 36 | } 37 | 38 | void TestDatabase::Test2AdHoc() { 39 | cout << __PRETTY_FUNCTION__ << endl; 40 | 41 | bool wasCaught = false; 42 | try { 43 | Database db("localhost", "root", "", "sakila", 0, NULL, 0); 44 | db.Connect(); 45 | 46 | UTASSERT(db.IsConnected()); 47 | 48 | AdhocStatement stmt(db, "SELECT * from COUNTRY"); 49 | stmt.Execute(); 50 | 51 | int testId = 0; 52 | std::string lastCountry; 53 | 54 | while (stmt.FetchNextRow()) { 55 | testId++; 56 | Nullable countryId = stmt.GetUShortDataInRow(0); 57 | Nullable countryName = stmt.GetStringDataInRow(1); 58 | Nullable lastUpdate = stmt.GetTimeDataInRow(2); 59 | 60 | UTASSERT(testId == (*countryId)); 61 | GregorianBreakdown gb = lastUpdate->to_gregorian(0); 62 | UTASSERT(gb.year == 2006); 63 | UTASSERT(gb.month == 2); 64 | UTASSERT(gb.day == 15); 65 | 66 | if (testId > 1) { 67 | UTASSERT(countryName.deref() > lastCountry); 68 | } 69 | lastCountry = countryName.deref(); 70 | } 71 | 72 | UTASSERT(testId == 109); 73 | 74 | } catch (const DatabaseException &de) { 75 | cout << de << endl; 76 | wasCaught = true; 77 | } catch (const UTFail &fail) { 78 | throw; 79 | } catch (const std::exception &exp) { 80 | cout << exp.what() << endl; 81 | wasCaught = true; 82 | } catch (...) { 83 | cout << "random exception caught" << endl; 84 | wasCaught = true; 85 | } 86 | 87 | UTASSERT(! wasCaught); 88 | } 89 | 90 | void TestDatabase::Test2AdHoc2() { 91 | cout << __PRETTY_FUNCTION__ << endl; 92 | 93 | bool wasCaught = false; 94 | try { 95 | Database db("localhost", "root", "", "sakila", 0, NULL, 0); 96 | db.Connect(); 97 | 98 | UTASSERT(db.IsConnected()); 99 | 100 | string sql = "SELECT * FROM COUNTRY WHERE COUNTRY = '\xC2\xA1\xC2\xBFHasta ma\xC3\xB1\x61na!?'"; 101 | { 102 | AdhocStatement stmt(db, sql); 103 | UTASSERT(stmt.RemainingParameters() == 0); 104 | stmt.Execute(); 105 | 106 | UTASSERT(! stmt.FetchNextRow()); 107 | } 108 | 109 | sql = "SELECT * FROM COUNTRY WHERE COUNTRY = ?"; 110 | { 111 | AdhocStatement stmt2(db, sql); 112 | UTASSERT(stmt2.RemainingParameters() == 1); 113 | 114 | bool stmt2WasCaught = false; 115 | try { 116 | stmt2.Execute(); 117 | } catch (const DatabaseException &de) { 118 | stmt2WasCaught = true; 119 | char stmt2line[512]; 120 | std::stringstream ss; 121 | ss << de << endl; 122 | ss.getline(stmt2line, 512); 123 | UTASSERT(strcmp(stmt2line, "Error in AdhocStatement::Execute ERROR 0(----) There are stil some unsatisfied parameters") == 0); 124 | } 125 | UTASSERT(stmt2WasCaught == true); 126 | 127 | stmt2.AssignNextParameter(Nullable("Canada")); 128 | stmt2.Execute(); 129 | UTASSERT(stmt2.NumberOfReturnedRows() == 1); 130 | UTASSERT(stmt2.FetchNextRow()); 131 | 132 | Nullable countryId = stmt2.GetUShortDataInRow(0); 133 | Nullable countryName = stmt2.GetStringDataInRow(1); 134 | Nullable lastUpdate = stmt2.GetTimeDataInRow(2); 135 | 136 | UTASSERT(*countryId == 20); 137 | UTASSERT(strcmp(countryName->c_str(), "Canada") == 0); 138 | GregorianBreakdown gb = lastUpdate->to_gregorian(0); 139 | UTASSERT(gb.year == 2006); 140 | UTASSERT(gb.month == 2); 141 | UTASSERT(gb.day == 15); 142 | } 143 | 144 | } catch (const DatabaseException &de) { 145 | cout << de << endl; 146 | wasCaught = true; 147 | } catch (const UTFail &fail) { 148 | throw; 149 | } catch (const std::exception &exp) { 150 | cout << exp.what() << endl; 151 | wasCaught = true; 152 | } catch (...) { 153 | cout << "random exception caught" << endl; 154 | wasCaught = true; 155 | } 156 | 157 | UTASSERT(! wasCaught); 158 | } 159 | 160 | void TestDatabase::Test2() { 161 | cout << __PRETTY_FUNCTION__ << endl; 162 | 163 | bool wasCaught = false; 164 | try { 165 | Database db("localhost", "root", "", "sakila", 0, NULL, 0); 166 | db.Connect(); 167 | 168 | UTASSERT(db.IsConnected()); 169 | 170 | Statement stmt(db, "SELECT * from COUNTRY"); 171 | stmt.Execute(); 172 | 173 | int testId = 0; 174 | std::string lastCountry; 175 | 176 | while (stmt.FetchNextRow()) { 177 | testId++; 178 | Nullable countryId = stmt.GetUShortDataInRow(0); 179 | Nullable countryName = stmt.GetStringDataInRow(1); 180 | Nullable lastUpdate = stmt.GetTimeDataInRow(2); 181 | 182 | UTASSERT(testId == (*countryId)); 183 | 184 | GregorianBreakdown gb = lastUpdate->to_gregorian(0); 185 | UTASSERT(gb.year == 2006); 186 | UTASSERT(gb.month == 2); 187 | UTASSERT(gb.day == 15); 188 | 189 | if (testId > 1) { 190 | UTASSERT(countryName.deref() > lastCountry); 191 | } 192 | lastCountry = countryName.deref(); 193 | } 194 | 195 | UTASSERT(testId == 109); 196 | 197 | } catch (const DatabaseException &de) { 198 | cout << de << endl; 199 | wasCaught = true; 200 | } catch (const UTFail &fail) { 201 | throw; 202 | } catch (const JulianException &je) { 203 | cout << je.what() << endl; 204 | wasCaught = true; 205 | } catch (...) { 206 | cout << "random exception caught" << endl; 207 | wasCaught = true; 208 | } 209 | 210 | UTASSERT(! wasCaught); 211 | } 212 | 213 | void TestDatabase::Test3() { 214 | cout << __PRETTY_FUNCTION__ << endl; 215 | 216 | bool wasCaught = false; 217 | try { 218 | Database db("localhost", "root", "", "sakila", 0, NULL, 0); 219 | db.Connect(); 220 | 221 | UTASSERT(db.IsConnected()); 222 | 223 | Statement stmt(db, "SELECT * from COUNTRY where country_id = ?"); 224 | stmt << Nullable(7) << execute; 225 | 226 | short int testId = 6; 227 | std::string lastCountry; 228 | 229 | while (stmt << fetch) { 230 | testId++; 231 | Nullable countryId; 232 | Nullable countryName; 233 | Nullable lastUpdate; 234 | 235 | stmt >> countryId >> countryName >> lastUpdate; 236 | 237 | UTASSERT(testId == (*countryId)); 238 | GregorianBreakdown gb = lastUpdate->to_gregorian(0); 239 | UTASSERT(gb.year == 2006); 240 | UTASSERT(gb.month == 2); 241 | UTASSERT(gb.day == 15); 242 | 243 | if (testId > 1) { 244 | UTASSERT(countryName.deref() > lastCountry); 245 | } 246 | lastCountry = countryName.deref(); 247 | } 248 | 249 | UTASSERT(testId == 7); 250 | 251 | } catch (const DatabaseException &de) { 252 | cout << de << endl; 253 | wasCaught = true; 254 | } 255 | 256 | UTASSERT(! wasCaught); 257 | } 258 | 259 | void TestDatabase::Test4() { 260 | cout << __PRETTY_FUNCTION__ << endl; 261 | 262 | bool caught = false; 263 | 264 | struct stat filestat; 265 | UTASSERT(stat("mike.jpg", &filestat) == 0); 266 | 267 | ifstream myFile("pic.jpg", ios::in | ios::binary); 268 | myFile.exceptions(std::ios::failbit); 269 | unsigned char *picBuf = (unsigned char *) malloc(filestat.st_size); 270 | myFile.read((char *)picBuf, filestat.st_size); 271 | myFile.close(); 272 | 273 | Binary picFromDisk(picBuf, filestat.st_size, filestat.st_size); 274 | 275 | try { 276 | Database db("localhost", "root", "", "sakila", 0, NULL, 0); 277 | db.Connect(); 278 | Statement stmt(db, "select picture from staff where first_name = ?"); 279 | stmt << Nullable("Mike") << execute; 280 | 281 | UTASSERT(stmt << fetch); 282 | 283 | Nullable mikepic; 284 | stmt >> mikepic; 285 | 286 | UTASSERT(mikepic.HasValue()); 287 | UTASSERT(mikepic->BufferLength() == 36365); 288 | UTASSERT((mikepic.const_deref()) == picFromDisk); 289 | } catch (const DatabaseException &de) { 290 | cout << de << endl; 291 | caught = true; 292 | } catch (const NullableException &ne) { 293 | cout << ne << endl; 294 | caught = true; 295 | } 296 | 297 | UTASSERT(! caught); 298 | } 299 | 300 | void TestDatabase::Test5() { 301 | cout << __PRETTY_FUNCTION__ << endl; 302 | 303 | bool wasCaught = false; 304 | try { 305 | Database db("localhost", "root", "", "sakila", 0, NULL, 0); 306 | db.Connect(); 307 | 308 | UTASSERT(db.IsConnected()); 309 | 310 | Statement stmt(db, "SELECT * from COUNTRY where country_id = ?"); 311 | stmt << Nullable(7) << execute; 312 | 313 | UTASSERT(stmt << fetch); 314 | UTASSERT(! stmt.Eof()); 315 | 316 | Nullable countryId; 317 | Nullable countryName; 318 | Nullable lastUpdate; 319 | 320 | stmt >> countryId >> countryName >> lastUpdate; 321 | 322 | UTASSERT(7 == (*countryId)); 323 | UTASSERT(strcmp(countryName->c_str(), "Armenia") == 0); 324 | GregorianBreakdown gb = lastUpdate->to_gregorian(0); 325 | UTASSERT(gb.year == 2006); 326 | UTASSERT(gb.month == 2); 327 | UTASSERT(gb.day == 15); 328 | 329 | stmt << reset << Nullable(8) << execute; 330 | UTASSERT(stmt << fetch); 331 | UTASSERT(! stmt.Eof()); 332 | 333 | stmt >> countryId >> countryName >> lastUpdate; 334 | 335 | UTASSERT(8 == (*countryId)); 336 | UTASSERT(strcmp(countryName->c_str(), "Australia") == 0); 337 | gb = lastUpdate->to_gregorian(0); 338 | UTASSERT(gb.year == 2006); 339 | UTASSERT(gb.month == 2); 340 | UTASSERT(gb.day == 15); 341 | 342 | } catch (const DatabaseException &de) { 343 | cout << de << endl; 344 | wasCaught = true; 345 | } 346 | 347 | UTASSERT(! wasCaught); 348 | } 349 | 350 | 351 | void TestDatabase::Test6() { 352 | cout << __PRETTY_FUNCTION__ << endl; 353 | 354 | bool wasCaught = false; 355 | try { 356 | Database db("localhost", "root", "", "sakila", 0, NULL, 0); 357 | db.Connect(); 358 | 359 | UTASSERT(db.IsConnected()); 360 | 361 | Statement stmt(db, "SELECT film_id, release_year from FILM where film_id = ?"); 362 | stmt << Nullable(100) << execute; 363 | 364 | UTASSERT(stmt << fetch); 365 | UTASSERT(! stmt.Eof()); 366 | 367 | Nullable filmId; 368 | Nullable releaseYear; 369 | 370 | stmt >> filmId >> releaseYear; 371 | UTASSERT(*filmId == (unsigned short) 100); 372 | UTASSERT(*releaseYear == (unsigned short) 2006); 373 | 374 | stmt << reset << Nullable(101) << execute; 375 | 376 | UTASSERT(stmt << fetch); 377 | UTASSERT(! stmt.Eof()); 378 | 379 | stmt >> filmId >> releaseYear; 380 | UTASSERT(*filmId == (unsigned short) 101); 381 | UTASSERT(*releaseYear == (unsigned short) 2006); 382 | 383 | } catch (const DatabaseException &de) { 384 | cout << de << endl; 385 | wasCaught = true; 386 | } 387 | 388 | UTASSERT(! wasCaught); 389 | } 390 | 391 | void TestDatabase::Test7() { 392 | cout << __PRETTY_FUNCTION__ << endl; 393 | 394 | bool wasCaught = false; 395 | try { 396 | Database db("localhost", "root", "", "sakila", 0, NULL, 0); 397 | db.Connect(); 398 | 399 | UTASSERT(db.IsConnected()); 400 | 401 | db.Execute("DROP PROCEDURE IF EXISTS rewards_report2"); 402 | 403 | db.Execute("CREATE PROCEDURE rewards_report2 ( IN min_monthly_purchases TINYINT UNSIGNED , IN min_dollar_amount_purchased DECIMAL(10,2) UNSIGNED , OUT count_rewardees INT ) LANGUAGE SQL NOT DETERMINISTIC READS SQL DATA SQL SECURITY DEFINER COMMENT 'Provides a customizable report on best customers' proc: BEGIN DECLARE last_month_start DATE; DECLARE last_month_end DATE; IF min_monthly_purchases = 0 THEN SELECT 'Minimum monthly purchases parameter must be > 0'; LEAVE proc; END IF; IF min_dollar_amount_purchased = 0.00 THEN SELECT 'Minimum monthly dollar amount purchased parameter must be > $0.00'; LEAVE proc; END IF; SET last_month_start = DATE_SUB(CURRENT_DATE(), INTERVAL 1 MONTH); SET last_month_start = STR_TO_DATE(CONCAT(YEAR(last_month_start),'-',MONTH(last_month_start),'-01'),'%Y-%m-%d'); SET last_month_end = LAST_DAY(last_month_start); CREATE TEMPORARY TABLE tmpCustomer (customer_id SMALLINT UNSIGNED NOT NULL PRIMARY KEY); INSERT INTO tmpCustomer (customer_id) SELECT p.customer_id FROM payment AS p WHERE DATE(p.payment_date) BETWEEN last_month_start AND last_month_end GROUP BY customer_id HAVING SUM(p.amount) > min_dollar_amount_purchased AND COUNT(customer_id) > min_monthly_purchases; SELECT COUNT(*) FROM tmpCustomer INTO count_rewardees; SELECT c.* FROM tmpCustomer AS t INNER JOIN customer AS c ON t.customer_id = c.customer_id; DROP TABLE tmpCustomer; END"); 404 | 405 | } catch (const DatabaseException &de) { 406 | cout << de << endl; 407 | wasCaught = true; 408 | } 409 | 410 | UTASSERT(! wasCaught); 411 | } 412 | 413 | void TestDatabase::Test8() { 414 | cout << __PRETTY_FUNCTION__ << endl; 415 | 416 | bool wasCaught = false; 417 | try { 418 | Database db_initial("localhost", "root", "", "sakila", 0, NULL, 0); 419 | Database db(db_initial); 420 | db.Connect(); 421 | 422 | UTASSERT(db.IsConnected()); 423 | 424 | Statement stmt(db, "SELECT * from COUNTRY"); 425 | stmt.Execute(); 426 | 427 | int testId = 0; 428 | std::string lastCountry; 429 | 430 | while (stmt.FetchNextRow()) { 431 | testId++; 432 | Nullable countryId = stmt.GetUShortDataInRow(0); 433 | Nullable countryName = stmt.GetStringDataInRow(1); 434 | Nullable lastUpdate = stmt.GetTimeDataInRow(2); 435 | 436 | UTASSERT(testId == (*countryId)); 437 | GregorianBreakdown gb = lastUpdate->to_gregorian(0); 438 | UTASSERT(gb.year == 2006); 439 | UTASSERT(gb.month == 2); 440 | UTASSERT(gb.day == 15); 441 | 442 | if (testId > 1) { 443 | UTASSERT(countryName.deref() > lastCountry); 444 | } 445 | lastCountry = countryName.deref(); 446 | } 447 | 448 | UTASSERT(testId == 109); 449 | 450 | } catch (const DatabaseException &de) { 451 | cout << de << endl; 452 | wasCaught = true; 453 | } catch (const UTFail &fail) { 454 | throw; 455 | } catch (...) { 456 | cout << "random exception caught" << endl; 457 | wasCaught = true; 458 | } 459 | 460 | UTASSERT(! wasCaught); 461 | } 462 | 463 | void TestDatabase::Test9() { 464 | cout << __PRETTY_FUNCTION__ << endl; 465 | bool wasCaught = false; 466 | try { 467 | Database db_initial("localhost", "root", "", "sakila", 0, NULL, 0); 468 | db_initial.Connect(); 469 | Database db(db_initial); 470 | UTASSERT(db.IsConnected()); 471 | } catch (const DatabaseException &de) { 472 | cout << de << endl; 473 | wasCaught = true; 474 | } catch (const UTFail &fail) { 475 | throw; 476 | } catch (...) { 477 | cout << "random exception caught" << endl; 478 | wasCaught = true; 479 | } 480 | 481 | UTASSERT(! wasCaught); 482 | } 483 | 484 | void TestDatabase::Test10() { 485 | cout << __PRETTY_FUNCTION__ << endl; 486 | bool wasCaught = false; 487 | try { 488 | Database db ("localhost", "root", "", "sakila", 0, NULL, 0); 489 | db.Connect(); 490 | 491 | Statement stmt(db, "SELECT EMAIL, CREATE_DATE FROM CUSTOMER WHERE CREATE_DATE = ?"); 492 | 493 | Julian createDate(2006, 02, 14, 22, 04, 36, 0); 494 | stmt << Nullable(createDate) << execute << fetch; 495 | 496 | UTEQUALS(stmt.NumberOfReturnedRows(), 271); 497 | } catch (const DatabaseException &de) { 498 | cout << de << endl; 499 | wasCaught = true; 500 | } catch (const UTFail &fail) { 501 | throw; 502 | } catch (const JulianException &je) { 503 | cout << je.what() << endl; 504 | wasCaught = true; 505 | } catch (...) { 506 | cout << "random failure" << endl; 507 | wasCaught = true; 508 | } 509 | 510 | UTASSERT(! wasCaught); 511 | } 512 | 513 | void TestDatabase::Test10AdHoc() { 514 | cout << __PRETTY_FUNCTION__ << endl; 515 | 516 | bool wasCaught = false; 517 | try { 518 | Database db ("localhost", "root", "", "sakila", 0, NULL, 0); 519 | db.Connect(); 520 | 521 | AdhocStatement stmt(db, "SELECT EMAIL, CREATE_DATE FROM CUSTOMER WHERE CREATE_DATE = ?"); 522 | 523 | Julian createDate(2006, 02, 14, 22, 04, 36, 0); 524 | stmt << Nullable(createDate) << execute << fetch; 525 | 526 | UTASSERT(stmt.NumberOfReturnedRows() == 271); 527 | } catch (const DatabaseException &de) { 528 | cout << de << endl; 529 | wasCaught = true; 530 | } catch (const UTFail &fail) { 531 | throw; 532 | } catch (const JulianException &je) { 533 | cout << je.what() << endl; 534 | wasCaught = true; 535 | } catch (...) { 536 | cout << "random failure" << endl; 537 | wasCaught = true; 538 | } 539 | 540 | UTASSERT(! wasCaught); 541 | } 542 | 543 | void TestDatabase::Test11() { 544 | cout << __PRETTY_FUNCTION__ << endl; 545 | 546 | bool wasCaught = false; 547 | try { 548 | Database db ("localhost", "root", "", "sakila", 0, NULL, 0); 549 | 550 | bool stmtCaught = false; 551 | try { 552 | Statement stmt(db, "SELECT EMAIL, CREATE_DATE FROM CUSTOMER WHERE CREATE_DATE = ?"); 553 | } catch (const DatabaseException &de) { 554 | stmtCaught = true; 555 | std::stringstream ss; 556 | ss << de << endl; 557 | char line[512]; 558 | ss.getline(line, 512); 559 | 560 | UTASSERT(strcmp(line, "Error in Statement::Prepare ERROR 0(----) Database is not connected") == 0); 561 | } 562 | 563 | UTASSERT(stmtCaught); 564 | } catch (const DatabaseException &de) { 565 | cout << de << endl; 566 | wasCaught = true; 567 | } catch (const JulianException &je) { 568 | cout << je.what() << endl; 569 | wasCaught = true; 570 | } catch (const UTFail &fail) { 571 | throw; 572 | } catch (...) { 573 | cout << "random failure" << endl; 574 | wasCaught = true; 575 | } 576 | 577 | UTASSERT(! wasCaught); 578 | } 579 | 580 | void TestDatabase::Test11AdHoc() { 581 | cout << __PRETTY_FUNCTION__ << endl; 582 | 583 | bool wasCaught = false; 584 | try { 585 | Database db ("localhost", "root", "", "sakila", 0, NULL, 0); 586 | 587 | bool stmtCaught = false; 588 | try { 589 | Statement stmt(db, "SELECT EMAIL, CREATE_DATE FROM CUSTOMER WHERE CREATE_DATE = ?"); 590 | } catch (const DatabaseException &de) { 591 | stmtCaught = true; 592 | std::stringstream ss; 593 | ss << de << endl; 594 | char line[512]; 595 | ss.getline(line, 512); 596 | 597 | UTASSERT(strcmp(line, "Error in Statement::Prepare ERROR 0(----) Database is not connected") == 0); 598 | } 599 | 600 | UTASSERT(stmtCaught); 601 | } catch (const DatabaseException &de) { 602 | cout << de << endl; 603 | wasCaught = true; 604 | } catch (const JulianException &je) { 605 | cout << je.what() << endl; 606 | wasCaught = true; 607 | } catch (const UTFail &fail) { 608 | throw; 609 | } catch (...) { 610 | cout << "random failure" << endl; 611 | wasCaught = true; 612 | } 613 | 614 | UTASSERT(! wasCaught); 615 | } 616 | 617 | void TestDatabase::Test12() { 618 | cout << __PRETTY_FUNCTION__ << endl; 619 | 620 | bool wasCaught = false; 621 | try { 622 | Database db ("localhost", "root", "", "sakila", 0, NULL, 0); 623 | db.Connect(); 624 | 625 | bool stmtCaught = false; 626 | try { 627 | Statement stmt(db, "SELECT ADDRESS, ADDRESS2 FROM ADDRESS WHERE ADDRESS2 = ?"); 628 | Nullable addr1; 629 | Nullable addr2; 630 | 631 | int count = 0; 632 | UTASSERT(stmt << addr2 << execute); 633 | while(stmt << fetch) { 634 | stmt >> addr1 >> addr2; 635 | UTASSERT(!addr2.HasValue()); 636 | count++; 637 | } 638 | 639 | UTASSERT(count == 0); 640 | 641 | } catch (const DatabaseException &de) { 642 | cout << de << endl; 643 | stmtCaught = true; 644 | } 645 | 646 | UTASSERT(! stmtCaught); 647 | } catch (const DatabaseException &de) { 648 | cout << de << endl; 649 | wasCaught = true; 650 | } catch (const JulianException &je) { 651 | cout << je.what() << endl; 652 | wasCaught = true; 653 | } catch (const UTFail &fail) { 654 | throw; 655 | } catch (...) { 656 | cout << "random failure" << endl; 657 | wasCaught = true; 658 | } 659 | 660 | UTASSERT(! wasCaught); 661 | } 662 | 663 | int TestDatabase::RunSpecificTest(DatabaseMemberPointer test) { 664 | int failures = 0; 665 | try { 666 | (this->*test)(); 667 | } catch (const UTFail &fail) { 668 | failures++; 669 | cout << fail << endl; 670 | } catch (...) { 671 | failures++; 672 | cout << "Some exception other than UTFAIL escaped from your UT!" << endl; 673 | } 674 | return failures; 675 | } 676 | 677 | int TestDatabase::RunTests(bool embedded) { 678 | int failures = 0; 679 | if (embedded) { 680 | failures += RunSpecificTest(&TestDatabase::Test1); 681 | failures += RunSpecificTest(&TestDatabase::Test2AdHoc); 682 | failures += RunSpecificTest(&TestDatabase::Test2AdHoc2); 683 | failures += RunSpecificTest(&TestDatabase::Test10AdHoc); 684 | failures += RunSpecificTest(&TestDatabase::Test11AdHoc); 685 | } else { 686 | failures += RunSpecificTest(&TestDatabase::Test1); 687 | failures += RunSpecificTest(&TestDatabase::Test2AdHoc); 688 | failures += RunSpecificTest(&TestDatabase::Test2AdHoc2); 689 | failures += RunSpecificTest(&TestDatabase::Test2); 690 | failures += RunSpecificTest(&TestDatabase::Test3); 691 | failures += RunSpecificTest(&TestDatabase::Test4); 692 | failures += RunSpecificTest(&TestDatabase::Test5); 693 | failures += RunSpecificTest(&TestDatabase::Test6); 694 | failures += RunSpecificTest(&TestDatabase::Test7); 695 | failures += RunSpecificTest(&TestDatabase::Test8); 696 | failures += RunSpecificTest(&TestDatabase::Test9); 697 | failures += RunSpecificTest(&TestDatabase::Test10); 698 | failures += RunSpecificTest(&TestDatabase::Test11); 699 | failures += RunSpecificTest(&TestDatabase::Test12); 700 | } 701 | return failures; 702 | } 703 | -------------------------------------------------------------------------------- /TestDatabase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Database.h" 5 | #include "UTFail.h" 6 | 7 | using namespace std; 8 | using namespace MySQLWrap; 9 | 10 | class TestDatabase; 11 | typedef void (TestDatabase::*DatabaseMemberPointer)(); 12 | 13 | class TestDatabase { 14 | public: 15 | TestDatabase(); 16 | int RunTests(bool embedded); 17 | private: 18 | void Test1(); 19 | void Test2(); 20 | void Test2AdHoc(); 21 | void Test2AdHoc2(); 22 | void Test3(); 23 | void Test4(); 24 | void Test5(); 25 | void Test6(); 26 | void Test7(); 27 | void Test8(); 28 | void Test9(); 29 | void Test10(); 30 | void Test10AdHoc(); 31 | void Test11(); 32 | void Test11AdHoc(); 33 | void Test12(); 34 | 35 | int RunSpecificTest(DatabaseMemberPointer test); 36 | }; 37 | -------------------------------------------------------------------------------- /TestDatabaseException.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "UTFail.h" 6 | #include "TestDatabaseException.h" 7 | 8 | #include 9 | 10 | using namespace std; 11 | 12 | TestDatabaseException::TestDatabaseException() {} 13 | 14 | void TestDatabaseException::Test1(bool embedded) { 15 | cout << __PRETTY_FUNCTION__ << endl; 16 | 17 | DatabaseException de("initial", 0, "9988", "end"); 18 | stringstream ss; 19 | ss << de << endl; 20 | char line[512]; 21 | ss.getline(line, 512); 22 | 23 | UTASSERT(strcmp(line, "initial ERROR 0(9988) end") == 0); 24 | } 25 | 26 | void TestDatabaseException::Test2(bool embedded) { 27 | cout << __PRETTY_FUNCTION__ << endl; 28 | 29 | if (embedded) return; 30 | MYSQL *db = mysql_init(NULL); 31 | UTASSERT(mysql_real_connect(db, "localhost", "root", "", "baddb", 0, NULL, 0) == NULL) 32 | DatabaseException de(db, "initial"); 33 | 34 | stringstream ss; 35 | ss << de; 36 | char line[5120]; 37 | ss.getline(line, 5120); 38 | 39 | UTASSERT(strcmp(line, "initial ERROR 1049(42000) Unknown database 'baddb'") == 0); 40 | mysql_close(db); 41 | } 42 | 43 | void TestDatabaseException::Test3(bool embedded) { 44 | cout << __PRETTY_FUNCTION__ << endl; 45 | 46 | if (embedded) return; 47 | MYSQL *db = mysql_init(NULL); 48 | UTASSERT(mysql_real_connect(db, "localhost", "root", "", "sakila", 0, NULL, 0) != NULL) 49 | MYSQL_STMT *stmt = mysql_stmt_init(db); 50 | 51 | const char *stmt_str = "SELECT * FROM BADTABLE"; 52 | UTASSERT(mysql_stmt_prepare(stmt, stmt_str, strlen(stmt_str)) != 0); 53 | DatabaseException de(stmt, "initial"); 54 | 55 | stringstream ss; 56 | ss << de; 57 | char line[512]; 58 | ss.getline(line, 512); 59 | 60 | UTASSERT(strcmp(line, "initial ERROR 1146(42S02) Table 'sakila.badtable' doesn't exist") == 0); 61 | mysql_stmt_close(stmt); 62 | mysql_close(db); 63 | } 64 | 65 | void TestDatabaseException::Test4(bool embedded) { 66 | cout << __PRETTY_FUNCTION__ << endl; 67 | } 68 | 69 | void TestDatabaseException::Test5(bool embedded) { 70 | cout << __PRETTY_FUNCTION__ << endl; 71 | } 72 | 73 | 74 | void TestDatabaseException::Test6(bool embedded) { 75 | cout << __PRETTY_FUNCTION__ << endl; 76 | } 77 | 78 | void TestDatabaseException::Test7(bool embedded) { 79 | cout << __PRETTY_FUNCTION__ << endl; 80 | } 81 | 82 | int TestDatabaseException::RunSpecificTest(DatabaseExceptionMemberPointer test, bool embedded) { 83 | int failures = 0; 84 | try { 85 | (this->*test)(embedded); 86 | } catch (UTFail &fail) { 87 | failures++; 88 | cout << fail << endl; 89 | } 90 | return failures; 91 | } 92 | 93 | int TestDatabaseException::RunTests(bool embedded) { 94 | int failures = 0; 95 | failures += RunSpecificTest(&TestDatabaseException::Test1, embedded); 96 | failures += RunSpecificTest(&TestDatabaseException::Test2, embedded); 97 | failures += RunSpecificTest(&TestDatabaseException::Test3, embedded); 98 | failures += RunSpecificTest(&TestDatabaseException::Test4, embedded); 99 | failures += RunSpecificTest(&TestDatabaseException::Test5, embedded); 100 | failures += RunSpecificTest(&TestDatabaseException::Test6, embedded); 101 | failures += RunSpecificTest(&TestDatabaseException::Test7, embedded); 102 | return failures; 103 | } 104 | -------------------------------------------------------------------------------- /TestDatabaseException.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "DatabaseException.h" 5 | #include "UTFail.h" 6 | 7 | using namespace std; 8 | using namespace MySQLWrap; 9 | 10 | class TestDatabaseException; 11 | typedef void (TestDatabaseException::*DatabaseExceptionMemberPointer)(bool embedded); 12 | 13 | class TestDatabaseException { 14 | public: 15 | TestDatabaseException(); 16 | int RunTests(bool embedded); 17 | private: 18 | void Test1(bool embedded); 19 | void Test2(bool embedded); 20 | void Test3(bool embedded); 21 | void Test4(bool embedded); 22 | void Test5(bool embedded); 23 | void Test6(bool embedded); 24 | void Test7(bool embedded); 25 | 26 | int RunSpecificTest(DatabaseExceptionMemberPointer test, bool embedded); 27 | }; 28 | -------------------------------------------------------------------------------- /TestImport.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "UTFail.h" 6 | #include "TestImport.h" 7 | #include "Database.h" 8 | #include "DatabaseException.h" 9 | #include "Statement.h" 10 | 11 | #include 12 | #include 13 | 14 | using namespace std; 15 | using namespace MySQLWrap; 16 | 17 | TestImport::TestImport() {} 18 | 19 | void TestImport::Test1() { 20 | cout << __PRETTY_FUNCTION__ << endl; 21 | 22 | char line[512]; 23 | bool wasCaught = false; 24 | try { 25 | Database db("localhost", "root", "", "", 0, NULL, 0); 26 | db.Connect(); 27 | 28 | db.Execute("drop database if exists sakila"); 29 | } catch (const DatabaseException &de) { 30 | std::stringstream ss; 31 | ss << de << endl; 32 | ss.getline(line, 512); 33 | wasCaught = true; 34 | cout << de << endl; 35 | } 36 | 37 | UTASSERT(! wasCaught); 38 | } 39 | 40 | void TestImport::Test2() { 41 | cout << __PRETTY_FUNCTION__ << endl; 42 | 43 | char line[512]; 44 | bool wasCaught = false; 45 | try { 46 | Database db("localhost", "root", "", "", 0, NULL, 0); 47 | db.Connect(); 48 | 49 | db.Source("/Users/ravidesai/Dropbox/cpp/mysqlwrap/sakila-db/sakila-schema.sql"); 50 | 51 | } catch (const DatabaseException &de) { 52 | std::stringstream ss; 53 | ss << de << endl; 54 | ss.getline(line, 512); 55 | wasCaught = true; 56 | cout << de << endl; 57 | } 58 | 59 | UTASSERT(! wasCaught); 60 | } 61 | 62 | void TestImport::Test3() { 63 | cout << __PRETTY_FUNCTION__ << endl; 64 | 65 | char line[512]; 66 | bool wasCaught = false; 67 | try { 68 | Database db("localhost", "root", "", "sakila", 0, NULL, 0); 69 | db.Connect(); 70 | 71 | db.Source("/Users/ravidesai/Dropbox/cpp/mysqlwrap/sakila-db/sakila-data.sql"); 72 | 73 | } catch (const DatabaseException &de) { 74 | std::stringstream ss; 75 | ss << de << endl; 76 | ss.getline(line, 512); 77 | wasCaught = true; 78 | cout << de << endl; 79 | } 80 | 81 | UTASSERT(! wasCaught); 82 | } 83 | 84 | void TestImport::Test4() { 85 | cout << __PRETTY_FUNCTION__ << endl; 86 | 87 | char line[512]; 88 | bool wasCaught = false; 89 | try { 90 | Database db("localhost", "root", "", "sakila", 0, NULL, 0); 91 | db.Connect(); 92 | 93 | db.Execute("select * from country"); 94 | 95 | } catch (const DatabaseException &de) { 96 | std::stringstream ss; 97 | ss << de << endl; 98 | ss.getline(line, 512); 99 | wasCaught = true; 100 | } 101 | 102 | UTASSERT(wasCaught); 103 | UTASSERT(strcmp(line, "Error in Database::Execute ERROR 0(----) statements passed to Execute should never return results.") == 0); 104 | } 105 | 106 | void TestImport::Test5() { 107 | cout << __PRETTY_FUNCTION__ << endl; 108 | } 109 | 110 | 111 | void TestImport::Test6() { 112 | cout << __PRETTY_FUNCTION__ << endl; 113 | } 114 | 115 | void TestImport::Test7() { 116 | cout << __PRETTY_FUNCTION__ << endl; 117 | } 118 | 119 | int TestImport::RunSpecificTest(ImportMemberPointer test) { 120 | int failures = 0; 121 | try { 122 | (this->*test)(); 123 | } catch (const UTFail &fail) { 124 | failures++; 125 | cout << fail << endl; 126 | } catch (...) { 127 | failures++; 128 | cout << "Some exception other than UTFAIL escaped from your UT!" << endl; 129 | } 130 | return failures; 131 | } 132 | 133 | int TestImport::RunTests(bool embedded) { 134 | int failures = 0; 135 | if (embedded) { 136 | failures += RunSpecificTest(&TestImport::Test1); 137 | failures += RunSpecificTest(&TestImport::Test2); 138 | failures += RunSpecificTest(&TestImport::Test3); 139 | } 140 | failures += RunSpecificTest(&TestImport::Test4); 141 | failures += RunSpecificTest(&TestImport::Test5); 142 | failures += RunSpecificTest(&TestImport::Test6); 143 | failures += RunSpecificTest(&TestImport::Test7); 144 | return failures; 145 | } 146 | -------------------------------------------------------------------------------- /TestImport.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "UTFail.h" 5 | 6 | using namespace std; 7 | 8 | class TestImport; 9 | typedef void (TestImport::*ImportMemberPointer)(); 10 | 11 | class TestImport { 12 | public: 13 | TestImport(); 14 | int RunTests(bool embedded); 15 | private: 16 | void Test1(); 17 | void Test2(); 18 | void Test3(); 19 | void Test4(); 20 | void Test5(); 21 | void Test6(); 22 | void Test7(); 23 | 24 | int RunSpecificTest(ImportMemberPointer test); 25 | }; 26 | -------------------------------------------------------------------------------- /TestJulian.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "UTFail.h" 6 | #include "TestJulian.h" 7 | #include "Julian.h" 8 | 9 | using namespace std; 10 | 11 | TestJulian::TestJulian() {} 12 | 13 | void TestJulian::Test1() { 14 | cout << __PRETTY_FUNCTION__ << endl; 15 | 16 | Julian j(1969, 07, 20, 20, 18, 0, 0); 17 | UTASSERT(j.Value() > 2440423.345833333 - 0.000001 && j.Value() < 2440423.345833333 + 0.000001); 18 | 19 | GregorianBreakdown gb = j.to_gregorian(0); 20 | 21 | UTASSERT(gb.year == 1969); 22 | UTASSERT(gb.month == 07); 23 | UTASSERT(gb.day == 20); 24 | UTASSERT(gb.hour == 20); 25 | UTASSERT(gb.minute == 18); 26 | UTASSERT(gb.second == 0); 27 | UTASSERT(gb.millisecond == 0); 28 | UTASSERT(gb.minutes_west_utc == 0); 29 | 30 | Julian one(-4713, 11, 25, 12, 0, 0, 0); 31 | UTASSERT(one.Value() > 0.999999 && one.Value() < 1.000001); 32 | 33 | gb.year = 2013; 34 | gb.month = 03; 35 | gb.day = 20; 36 | gb.hour = 6; 37 | gb.minute = 53; 38 | gb.second = 12; 39 | gb.millisecond = 540; 40 | gb.minutes_west_utc = 300; 41 | 42 | Julian mar20(gb); 43 | gb = mar20.to_gregorian(0); 44 | 45 | UTASSERT(gb.year == 2013); 46 | UTASSERT(gb.month = 03); 47 | UTASSERT(gb.day == 20); 48 | UTASSERT(gb.hour == 11); 49 | UTASSERT(gb.minute == 53); 50 | UTASSERT(gb.second == 12); 51 | UTASSERT(gb.millisecond == 540); 52 | UTASSERT(gb.minutes_west_utc == 0); 53 | 54 | gb = mar20.to_gregorian(300); 55 | 56 | UTASSERT(gb.year == 2013); 57 | UTASSERT(gb.month = 03); 58 | UTASSERT(gb.day == 20); 59 | UTASSERT(gb.hour == 06); 60 | UTASSERT(gb.minute == 53); 61 | UTASSERT(gb.second == 12); 62 | UTASSERT(gb.millisecond == 540); 63 | UTASSERT(gb.minutes_west_utc == 300); 64 | } 65 | 66 | void TestJulian::Test2() { 67 | cout << __PRETTY_FUNCTION__ << endl; 68 | 69 | Julian sixHours(0, 6,0,0,0); 70 | GregorianBreakdown gb = sixHours.to_gregorian(0); 71 | UTASSERT(gb.time_type == TimeType::Time); 72 | UTASSERT((gb.year == 0) && (gb.month == 0) && (gb.day == 0)); 73 | UTASSERT(gb.hour == 6); 74 | UTASSERT((gb.minute == 0) && (gb.second == 0) && (gb.millisecond == 0)); 75 | } 76 | 77 | void TestJulian::Test3() { 78 | cout << __PRETTY_FUNCTION__ << endl; 79 | for (int i = 0; i < 1000; i++) { 80 | Julian j(0, 0,0,0,i); 81 | GregorianBreakdown gb = j.to_gregorian(0); 82 | UTASSERT(gb.millisecond == i); 83 | } 84 | } 85 | 86 | void TestJulian::Test4() { 87 | cout << __PRETTY_FUNCTION__ << endl; 88 | GregorianBreakdown gb(2004, 03, 01, 0, 0, 0, 0, 0); 89 | Julian j(gb); 90 | GregorianBreakdown newgb = j.to_gregorian(300); 91 | 92 | UTASSERT(newgb.year == 2004); 93 | UTASSERT(newgb.month == 02); 94 | UTASSERT(newgb.day == 29); 95 | UTASSERT(newgb.hour == 19); 96 | UTASSERT(newgb.minute == 00); 97 | UTASSERT(newgb.second == 00); 98 | UTASSERT(newgb.millisecond == 00); 99 | UTASSERT(newgb.minutes_west_utc == 300); 100 | 101 | gb.year = 2003; 102 | Julian j2(gb); 103 | newgb = j2.to_gregorian(300); 104 | 105 | UTASSERT(newgb.year == 2003); 106 | UTASSERT(newgb.month == 02); 107 | UTASSERT(newgb.day == 28); 108 | UTASSERT(newgb.hour == 19); 109 | UTASSERT(newgb.minute == 00); 110 | UTASSERT(newgb.second == 00); 111 | UTASSERT(newgb.millisecond == 00); 112 | UTASSERT(newgb.minutes_west_utc == 300); 113 | } 114 | 115 | void TestJulian::Test5() { 116 | cout << __PRETTY_FUNCTION__ << endl; 117 | 118 | Julian jd(2010, 10, 13); 119 | Julian jt(0, 10, 50, 34, 352); 120 | 121 | Julian jj = jd + jt; 122 | GregorianBreakdown gb = jj.to_gregorian(0); 123 | 124 | UTASSERT(gb.year == 2010); 125 | UTASSERT(gb.month == 10); 126 | UTASSERT(gb.day == 13); 127 | UTASSERT(gb.hour == 10); 128 | UTASSERT(gb.minute == 50); 129 | UTASSERT(gb.second == 34); 130 | UTASSERT(gb.millisecond == 352); 131 | UTASSERT(gb.time_type == TimeType::DateTime); 132 | UTASSERT(gb.minutes_west_utc == 0); 133 | 134 | Julian jdt(2013, 3, 20, 19, 49, 27, 810); 135 | Julian jt2(0, 1, 0, 0, 200); 136 | 137 | jj = jdt + jt2; 138 | gb = jj.to_gregorian(0); 139 | 140 | UTASSERT(gb.year == 2013); 141 | UTASSERT(gb.month == 03); 142 | UTASSERT(gb.day == 20); 143 | UTASSERT(gb.hour == 20); 144 | UTASSERT(gb.minute == 49); 145 | UTASSERT(gb.second == 28); 146 | UTASSERT(gb.millisecond == 10); 147 | UTASSERT(gb.time_type == TimeType::DateTime); 148 | UTASSERT(gb.minutes_west_utc == 0); 149 | 150 | Julian jt3(0, 3, 4, 2, 400); 151 | Julian jt4(0, 1, 55, 57, 600); 152 | 153 | Julian jt5 = jt3 + jt4; 154 | gb = jt5.to_gregorian(0); 155 | 156 | UTASSERT(gb.hour == 5); 157 | UTASSERT(gb.minute == 0); 158 | UTASSERT(gb.second == 0); 159 | UTASSERT(gb.millisecond == 0); 160 | UTASSERT(gb.time_type == TimeType::Time); 161 | UTASSERT(gb.minutes_west_utc == 0); 162 | } 163 | 164 | 165 | void TestJulian::Test6() { 166 | cout << __PRETTY_FUNCTION__ << endl; 167 | 168 | Julian j(-43, 1, 15); 169 | GregorianBreakdown bc = j.to_gregorian(0); 170 | 171 | std::stringstream ss; 172 | ss << bc << endl; 173 | char line[512]; 174 | ss.getline(line, 512); 175 | UTASSERT(strcmp(line, "BCE 44-01-15") == 0); 176 | 177 | Julian year1(1, 1, 1, 0, 0, 0, 0); 178 | UTASSERT(1721425.499 < year1.Value() && year1.Value() < 1721425.501); 179 | 180 | Julian j2(1721424.5, TimeType::Date); 181 | GregorianBreakdown gb = j2.to_gregorian(0); 182 | std::stringstream s2; 183 | s2 << gb << endl; 184 | s2.getline(line, 512); 185 | UTASSERT(strcmp(line, "BCE 1-12-31") == 0); 186 | 187 | Julian today(2013, 03, 29, 12, 0, 0, 0); 188 | UTASSERT(today.Weekday(0) == DayOfWeek::Friday); 189 | } 190 | 191 | void TestJulian::Test7() { 192 | cout << __PRETTY_FUNCTION__ << endl; 193 | 194 | } 195 | 196 | int TestJulian::RunSpecificTest(JulianMemberPointer test) { 197 | int failures = 0; 198 | try { 199 | (this->*test)(); 200 | } catch (UTFail &fail) { 201 | failures++; 202 | cout << fail << endl; 203 | } 204 | return failures; 205 | } 206 | 207 | int TestJulian::RunTests() { 208 | int failures = 0; 209 | failures += RunSpecificTest(&TestJulian::Test1); 210 | failures += RunSpecificTest(&TestJulian::Test2); 211 | failures += RunSpecificTest(&TestJulian::Test3); 212 | failures += RunSpecificTest(&TestJulian::Test4); 213 | failures += RunSpecificTest(&TestJulian::Test5); 214 | failures += RunSpecificTest(&TestJulian::Test6); 215 | failures += RunSpecificTest(&TestJulian::Test7); 216 | return failures; 217 | } 218 | -------------------------------------------------------------------------------- /TestJulian.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Nullable.h" 5 | #include "UTFail.h" 6 | 7 | using namespace MySQLWrap; 8 | 9 | class TestJulian; 10 | typedef void (TestJulian::*JulianMemberPointer)(); 11 | 12 | class TestJulian { 13 | public: 14 | TestJulian(); 15 | int RunTests(); 16 | private: 17 | void Test1(); 18 | void Test2(); 19 | void Test3(); 20 | void Test4(); 21 | void Test5(); 22 | void Test6(); 23 | void Test7(); 24 | 25 | int RunSpecificTest(JulianMemberPointer test); 26 | }; 27 | -------------------------------------------------------------------------------- /TestNullable.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "UTFail.h" 3 | #include "TestNullable.h" 4 | 5 | using namespace std; 6 | 7 | TestNullable::TestNullable() {} 8 | 9 | void TestNullable::Test1() { 10 | cout << __PRETTY_FUNCTION__ << endl; 11 | 12 | Nullable ni; 13 | UTASSERT(ni.HasValue() == false); 14 | UTASSERT(!ni); 15 | 16 | bool caught = false; 17 | try { 18 | int i = *ni; 19 | UTASSERT(i == 0xBAD); // should never reach this line 20 | } catch (const NullableException &ne) { 21 | caught = true; 22 | } 23 | UTASSERT(caught == true); 24 | } 25 | 26 | void TestNullable::Test2() { 27 | cout << __PRETTY_FUNCTION__ << endl; 28 | 29 | Nullable ni(7); 30 | UTASSERT(ni.HasValue() == true); 31 | UTASSERT((!ni) == false); 32 | } 33 | 34 | void TestNullable::Test3() { 35 | cout << __PRETTY_FUNCTION__ << endl; 36 | 37 | Nullable ni; 38 | Nullable ni2(14); 39 | 40 | ni = ni2; 41 | UTASSERT(ni.HasValue()); 42 | UTASSERT(*ni == 14); 43 | 44 | ni.ClearValue(); 45 | UTASSERT(!ni); 46 | 47 | ni = 9; 48 | UTASSERT(ni.HasValue()); 49 | UTASSERT(*ni == 9); 50 | } 51 | 52 | void TestNullable::Test4() { 53 | cout << __PRETTY_FUNCTION__ << endl; 54 | 55 | Nullable nd1; 56 | Nullable nd2; 57 | 58 | UTASSERT(! nd1); 59 | UTASSERT(! nd2); 60 | UTASSERT(nd1 == nd2); 61 | UTASSERT(! (nd1 < nd2)); 62 | UTASSERT(nd1 <= nd2); 63 | UTASSERT(! (nd1 != nd2)); 64 | UTASSERT(nd1 >= nd2); 65 | } 66 | 67 | void TestNullable::Test5() { 68 | cout << __PRETTY_FUNCTION__ << endl; 69 | 70 | Nullable nsi1; 71 | Nullable nsi2(15); 72 | 73 | UTASSERT(! nsi1); 74 | UTASSERT(nsi2.HasValue()); 75 | 76 | UTASSERT(! (nsi1 == nsi2)); 77 | UTASSERT(nsi1 != nsi2); 78 | UTASSERT(nsi1 < nsi2); 79 | UTASSERT(nsi1 <= nsi2); 80 | UTASSERT(! (nsi1 > nsi2)); 81 | UTASSERT(! (nsi1 >= nsi2)); 82 | 83 | UTASSERT(! (nsi2 == nsi1)); 84 | UTASSERT(nsi2 != nsi1); 85 | UTASSERT(! (nsi2 < nsi1)); 86 | UTASSERT(! (nsi2 <= nsi1)); 87 | UTASSERT(nsi2 > nsi1); 88 | UTASSERT(nsi2 >= nsi1); 89 | } 90 | 91 | 92 | void TestNullable::Test6() { 93 | cout << __PRETTY_FUNCTION__ << endl; 94 | 95 | Nullable ns1; 96 | Nullable ns2("test"); 97 | ns1 = "test"; 98 | 99 | UTASSERT(ns1 == ns2); 100 | UTASSERT(! (ns1 != ns2)); 101 | UTASSERT(! (ns1 < ns2)); 102 | UTASSERT(ns1 <= ns2); 103 | UTASSERT(! (ns1 > ns2)); 104 | UTASSERT(ns1 >= ns2); 105 | 106 | UTASSERT(ns2 == ns1); 107 | UTASSERT(! (ns2 != ns1)); 108 | UTASSERT(! (ns2 < ns1)); 109 | UTASSERT(ns2 <= ns1); 110 | UTASSERT(! (ns2 > ns1)); 111 | UTASSERT(ns2 >= ns1); 112 | } 113 | 114 | void TestNullable::Test7() { 115 | cout << __PRETTY_FUNCTION__ << endl; 116 | 117 | Nullable ni1(5); 118 | Nullable ni2(19); 119 | 120 | UTASSERT(! (ni1 == ni2)); 121 | UTASSERT(ni1 != ni2); 122 | UTASSERT(ni1 < ni2); 123 | UTASSERT(ni1 <= ni2); 124 | UTASSERT(! (ni1 > ni2)); 125 | UTASSERT(! (ni1 >= ni2)); 126 | 127 | UTASSERT(! (ni2 == ni1)); 128 | UTASSERT(ni2 != ni1); 129 | UTASSERT(! (ni2 < ni1)); 130 | UTASSERT(! (ni2 <= ni1)); 131 | UTASSERT(ni2 > ni1); 132 | UTASSERT(ni2 >= ni1); 133 | } 134 | 135 | int TestNullable::RunSpecificTest(NullableMemberPointer test) { 136 | int failures = 0; 137 | try { 138 | (this->*test)(); 139 | } catch (UTFail &fail) { 140 | failures++; 141 | cout << fail << endl; 142 | } 143 | return failures; 144 | } 145 | 146 | int TestNullable::RunTests() { 147 | int failures = 0; 148 | failures += RunSpecificTest(&TestNullable::Test1); 149 | failures += RunSpecificTest(&TestNullable::Test2); 150 | failures += RunSpecificTest(&TestNullable::Test3); 151 | failures += RunSpecificTest(&TestNullable::Test4); 152 | failures += RunSpecificTest(&TestNullable::Test5); 153 | failures += RunSpecificTest(&TestNullable::Test6); 154 | failures += RunSpecificTest(&TestNullable::Test7); 155 | return failures; 156 | } 157 | -------------------------------------------------------------------------------- /TestNullable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "Nullable.h" 5 | #include "UTFail.h" 6 | 7 | using namespace std; 8 | using namespace MySQLWrap; 9 | 10 | class TestNullable; 11 | typedef void (TestNullable::*NullableMemberPointer)(); 12 | 13 | class TestNullable { 14 | public: 15 | TestNullable(); 16 | int RunTests(); 17 | private: 18 | void Test1(); 19 | void Test2(); 20 | void Test3(); 21 | void Test4(); 22 | void Test5(); 23 | void Test6(); 24 | void Test7(); 25 | 26 | int RunSpecificTest(NullableMemberPointer test); 27 | }; 28 | -------------------------------------------------------------------------------- /UTFail.cpp: -------------------------------------------------------------------------------- 1 | #include "UTFail.h" 2 | 3 | UTFail::UTFail(const string &func, int line, const string &msg) { 4 | function = func; 5 | message = msg; 6 | lineNumber = line; 7 | } 8 | 9 | ostream& operator<<(ostream &out, const UTFail &fail) { 10 | out << "UT Failure in " << fail.function << " line: " << fail.lineNumber << " (" << fail.message << ")."; 11 | return out; 12 | } 13 | 14 | -------------------------------------------------------------------------------- /UTFail.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | using namespace std; 6 | 7 | #define UTASSERT(EXP) if (!(EXP)) throw UTFail(__PRETTY_FUNCTION__, __LINE__, #EXP); 8 | #define UTEQUALS(LHS, RHS) if (!((LHS) == (RHS))) { cerr << "Failure: " << (LHS) << " == " << (RHS) << endl; throw UTFail(__PRETTY_FUNCTION__, __LINE__, #LHS + std::string(" != ") + #RHS); } 9 | 10 | class UTFail { 11 | public: 12 | UTFail(const string &func, int line, const string &msg); 13 | 14 | friend ostream& operator<<(ostream &out, const UTFail& fail); 15 | private: 16 | string function; 17 | string message; 18 | int lineNumber; 19 | }; 20 | 21 | -------------------------------------------------------------------------------- /bug.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "my_global.h" 9 | #include "mysql.h" 10 | 11 | using namespace std; 12 | 13 | static void print_stmt_error (MYSQL_STMT *stmt, const char *message) { 14 | fprintf (stderr, "%s\n", message); 15 | if (stmt != NULL) { 16 | fprintf(stderr, "Error %u (%s): %s\n", 17 | mysql_stmt_errno(stmt), 18 | mysql_stmt_sqlstate(stmt), 19 | mysql_stmt_error(stmt)); 20 | } 21 | } 22 | 23 | int main(int argc, char** argv) { 24 | bool embedded = false; 25 | if (argc > 1 && (strcmp(argv[1], "embedded") == 0)) { 26 | embedded = true; 27 | } 28 | 29 | if (embedded) { 30 | cout << "Hello you are connected in embedded mode" << endl; 31 | 32 | static const char *server_args[] = { "this_program", "--basedir=/usr/local/mysql", "--datadir=/Users/ravidesai/mysql/data", 33 | "--plugin-dir=/Users/ravidesai/mysql/plugins", "--log-error=/Users/ravidesai/mysql/tmp/test.err", 34 | "--pid-file=/Users/ravidesai/mysql/tmp/test.pid", 35 | "--key_buffer_size=32M", "--log-bin=/Users/ravidesai/mysql/log/logbin" 36 | "--log-bin-trust-function-creators=1" 37 | "--log-bin-trust-routine-creators=1" 38 | }; 39 | static const char *server_groups[] = { "embedded", "server", "this_program_SERVER", (char *) NULL }; 40 | 41 | if (mysql_library_init(sizeof(server_args) / sizeof(char *), (char**) server_args, (char **)server_groups) != 0) { 42 | cout << "Error in Database::Initialize" << endl; 43 | return 1; 44 | } 45 | } else { 46 | cout << "Hello you are connected in standard client (non-embedded) mode" << endl; 47 | if (mysql_library_init(0, NULL, NULL) != 0) { 48 | cout << "Error in Database::Initialize" << endl; 49 | return 1; 50 | } 51 | } 52 | 53 | cout << "thread_init" << endl; 54 | if (mysql_thread_init() != 0) { 55 | cout << "Error in Database::ThreadInitialize" << endl; 56 | return 1; 57 | } 58 | 59 | cout << "mysql init" << endl; 60 | MYSQL *connect; 61 | connect = mysql_init(NULL); 62 | if (! connect) { 63 | cout << "MySQL initialization failure" << endl; 64 | return 1; 65 | } 66 | 67 | if (embedded) { 68 | cout << "setting options" << endl; 69 | mysql_options(connect, MYSQL_READ_DEFAULT_GROUP, "embedded"); 70 | mysql_options(connect, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL); 71 | } 72 | 73 | cout << "real_connect" << endl; 74 | connect = mysql_real_connect(connect, "localhost", "root", "", "sakila", 0, NULL, 0); 75 | if (connect) 76 | { 77 | cout << "Connection succeeded" << endl; 78 | } else { 79 | cout << "connction failure!" << endl; 80 | return 1; 81 | } 82 | 83 | const char *stmt_str = "SELECT country, country_id, last_update FROM COUNTRY"; 84 | MYSQL_STMT *stmt; 85 | 86 | cout << "stmt init" << endl; 87 | stmt = mysql_stmt_init(connect); 88 | 89 | cout << "prepare" << endl; 90 | if (mysql_stmt_prepare(stmt, stmt_str, strlen(stmt_str)) != 0) { 91 | print_stmt_error(stmt, "Count not prepare SELECT statement"); 92 | return 1; 93 | } 94 | 95 | int numberParams = mysql_stmt_param_count(stmt); 96 | cout << "Number parameters: " << numberParams << endl; 97 | MYSQL_RES *metaData; 98 | if ((metaData = mysql_stmt_result_metadata(stmt)) == NULL) { 99 | print_stmt_error(stmt, "Error fetching stmt metadata"); 100 | } 101 | 102 | int numberFields = 0; 103 | MYSQL_FIELD *field; 104 | while ((field = mysql_fetch_field(metaData)) != NULL) { 105 | numberFields++; 106 | cout << "[" << field->name << ", " << field->type << "] "; 107 | } 108 | cout << endl; 109 | 110 | const int STRING_SIZE = 50; 111 | 112 | unsigned short countryId = 0; 113 | char countryName[STRING_SIZE]; 114 | unsigned long countryNameLen; 115 | MYSQL_TIME lastUpdate; 116 | memset((void *) &lastUpdate, 0, sizeof(MYSQL_TYPE_TIME)); 117 | my_bool is_null[numberFields]; 118 | my_bool error[numberFields]; 119 | size_t countryIdLength = sizeof(unsigned short); 120 | size_t lastUpdateLength = sizeof(MYSQL_TYPE_TIME); 121 | 122 | MYSQL_BIND bind[numberFields]; 123 | memset ((void *) &bind, 0, sizeof(bind)); 124 | 125 | bind[0].buffer_type = MYSQL_TYPE_STRING; 126 | bind[0].buffer = (void *) countryName; 127 | bind[0].is_null = &is_null[1]; 128 | bind[0].buffer_length = sizeof(countryName); 129 | bind[0].length = &countryNameLen; 130 | bind[0].error = &error[1]; 131 | 132 | bind[1].buffer_type = MYSQL_TYPE_SHORT; 133 | bind[1].buffer = (void *) &countryId; 134 | bind[1].buffer_length = sizeof(unsigned short); 135 | bind[1].length = &countryIdLength; 136 | bind[1].is_unsigned = 1; 137 | bind[1].is_null = &is_null[0]; 138 | bind[1].error = &error[0]; 139 | 140 | bind[2].buffer_type = MYSQL_TYPE_DATETIME; 141 | bind[2].buffer = (void *) &lastUpdate; 142 | bind[2].buffer_length = sizeof(MYSQL_TYPE_DATETIME); 143 | bind[2].length = &lastUpdateLength; 144 | bind[2].is_null = &is_null[2]; 145 | bind[2].error = &error[2]; 146 | 147 | cout << "binding results" << endl; 148 | if (mysql_stmt_bind_result(stmt, bind) != 0) { 149 | print_stmt_error(stmt, "Count not bind SELECT results"); 150 | return 1; 151 | } 152 | 153 | cout << "executing statement" << endl; 154 | if (mysql_stmt_execute(stmt) != 0) { 155 | print_stmt_error(stmt, "Could not execute SELECT statement"); 156 | return 1; 157 | } 158 | 159 | cout << "storing results" << endl; 160 | if (mysql_stmt_store_result(stmt) != 0) { 161 | print_stmt_error(stmt, "Could not execute STORE RESULT"); 162 | return 1; 163 | } 164 | 165 | cout << "fetching statement" << endl; 166 | while (mysql_stmt_fetch(stmt) == 0) { 167 | printf("[%d] ", countryId); 168 | printf("[%.*s] ", (int) countryNameLen, countryName); 169 | printf("[%04d-%02d-%02d %02d:%02d:%02d]", 170 | lastUpdate.year, lastUpdate.month, lastUpdate.day, 171 | lastUpdate.hour, lastUpdate.minute, lastUpdate.second); 172 | printf("\n"); 173 | } 174 | 175 | cout << "closing statement" << endl; 176 | mysql_stmt_close(stmt); 177 | cout << "closing database" << endl; 178 | mysql_close(connect); 179 | 180 | return 0; 181 | } 182 | -------------------------------------------------------------------------------- /bugreport.readme: -------------------------------------------------------------------------------- 1 | This program fails with a segmentation fault on Mac OS/X Mountain Lion version 10.8.2 when compiled against the embedded mysqld library. 2 | The same program succeeds on the same platform when compiled against the mysql threaded client libraries. 3 | 4 | The segfault occurs in the mysql_stmt_execute function. 5 | 6 | I used the prebuilt installer for MYSQL pulled from the official website entitled: mysql-5.5.29-osx10.6-x86_64.dmg 7 | I installed the sakila database (specifically the country table) and populated it from the default scripts provided on the MYSQL website 8 | 9 | I am using gcc version 4.2.1 (as provided by XCode 4.6 command line tools installer). 10 | I compile to a build directory (here just called 'build/'), you'll need to create it to use my g++ lines (below) verbatim. 11 | 12 | The working version of the program (bug) is compiled in the following way and run against the installed MYSQL database instance: 13 | The Makefile actually uses the 'mysql_config --include' and 'mysql_config --libs_r' scripts for many of the switches below: 14 | 15 | g++ -Wall -g -O0 -c bug.cpp -I/usr/local/mysql/include -Os -g -fno-common -fno-strict-aliasing -arch x86_64 -o build/bug.o 16 | g++ -Wall build/bug.o -I/usr/local/mysql/include -Os -g -fno-common -fno-strict-aliasing -arch x86_64 -o build/bug -L/usr/local/mysql/lib -lmysqlclient_r -lpthread -Lbuild/ 17 | 18 | The version of the program (bug) that produces a segfault is compiled in the following way and run against an embedded MYSQL database instance (created in my 19 | home directory using the /usr/local/mysql/scripts/mysql_install_db script) 20 | As before, the Makefile actually uses the 'mysql_config --include' and 'mysql_config --libmysqld-libs' scripts to obtain many of the swithes below: 21 | 22 | g++ -Wall -g -O0 -c bug.cpp -I/usr/local/mysql/include -Os -g -fno-common -fno-strict-aliasing -arch x86_64 -o build/bug.o 23 | g++ -Wall build/bug.o -I/usr/local/mysql/include -Os -g -fno-common -fno-strict-aliasing -arch x86_64 -o build/bug -L/usr/local/mysql/lib -lmysqld -lpthread -Lbuild/ 24 | 25 | 26 | ==== 27 | 28 | Additional info. The bug now appears only to be related to prepared statement when compiled against the embedded datbase libraries and returning a "string" type. 29 | When bug.cpp is modified to only return the country_id and/or last_update fields of the country table, the program completes normally. The error only seems 30 | to occur when the country field is part of the result set. I originally though the error has something to do with a parameterized where clause, so I updated 31 | bug 62367, but I see now that this is not the case. 32 | 33 | 34 | -------------------------------------------------------------------------------- /mike.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviDesai/mysqlwrap/214a401a0a53a31d7eefa6ebed52dceb5d083bb1/mike.jpg -------------------------------------------------------------------------------- /pic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviDesai/mysqlwrap/214a401a0a53a31d7eefa6ebed52dceb5d083bb1/pic.jpg -------------------------------------------------------------------------------- /program.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "my_global.h" 9 | #include "mysql.h" 10 | 11 | #include "Database.h" 12 | #include "DatabaseException.h" 13 | #include "Statement.h" 14 | #include "ParamBuffer.h" 15 | 16 | using namespace std; 17 | using namespace MySQLWrap; 18 | 19 | static void print_stmt_error (MYSQL_STMT *stmt, const char *message) { 20 | fprintf (stderr, "%s\n", message); 21 | if (stmt != NULL) { 22 | fprintf(stderr, "Error %u (%s): %s\n", 23 | mysql_stmt_errno(stmt), 24 | mysql_stmt_sqlstate(stmt), 25 | mysql_stmt_error(stmt)); 26 | } 27 | } 28 | 29 | int main(int argc, char** argv) { 30 | cout << "Hello" << endl; 31 | 32 | MYSQL *connect; 33 | connect = mysql_init(NULL); 34 | if (! connect) { 35 | cout << "MySQL initialization failure" << endl; 36 | return 1; 37 | } 38 | 39 | connect = mysql_real_connect(connect, "localhost", "root", "", "sakila", 0, NULL, 0); 40 | if (connect) 41 | { 42 | cout << "Connection succeeded" << endl; 43 | } 44 | 45 | MYSQL_RES *res_set; 46 | MYSQL_ROW row; 47 | 48 | mysql_query(connect,"SELECT * FROM COUNTRY"); 49 | res_set = mysql_store_result(connect); 50 | 51 | unsigned int numrows = mysql_num_rows(res_set); 52 | cout << "Number rows: " << numrows << endl; 53 | 54 | unsigned int num_fields = mysql_num_fields(res_set); 55 | 56 | MYSQL_FIELD *field = NULL; 57 | 58 | while ((field = mysql_fetch_field(res_set)) != NULL) { 59 | cout << "[" << field->name << ", " << field->type << "] "; 60 | } 61 | cout << endl; 62 | 63 | while ((row = mysql_fetch_row(res_set)) != NULL) { 64 | unsigned long *lengths = mysql_fetch_lengths(res_set); 65 | for (unsigned int i = 0; i < num_fields; i++) { 66 | printf("[%.*s] ", (int) lengths[i], 67 | row[i] ? row[i] : "NULL"); 68 | } 69 | printf("\n"); 70 | } 71 | 72 | const char *stmt_str = "SELECT * FROM COUNTRY WHERE COUNTRY = ?"; 73 | //int id; 74 | //char country[26]; 75 | //MYSQL_TIME my_datetime; 76 | MYSQL_STMT *stmt; 77 | 78 | stmt = mysql_stmt_init(connect); 79 | 80 | if (mysql_stmt_prepare(stmt, stmt_str, strlen(stmt_str)) != 0) { 81 | print_stmt_error(stmt, "Count not prepare SELECT statement"); 82 | return 1; 83 | } 84 | 85 | int numberParams = mysql_stmt_param_count(stmt); 86 | cout << "Number parameters: " << numberParams << endl; 87 | MYSQL_BIND param[numberParams]; 88 | MYSQL_RES *metaData; 89 | if ((metaData = mysql_stmt_result_metadata(stmt)) == NULL) { 90 | print_stmt_error(stmt, "Error fetching stmt metadata"); 91 | } 92 | 93 | int numberFields = 0; 94 | while ((field = mysql_fetch_field(metaData)) != NULL) { 95 | numberFields++; 96 | cout << "[" << field->name << ", " << field->type << "] "; 97 | } 98 | cout << endl; 99 | 100 | const int STRING_SIZE = 50; 101 | 102 | int countryId; 103 | char countryName[STRING_SIZE]; 104 | unsigned long countryNameLen; 105 | MYSQL_TIME lastUpdate; 106 | my_bool is_null[numberFields]; 107 | 108 | MYSQL_BIND bind[numberFields]; 109 | memset ((void *) &bind, 0, sizeof(bind)); 110 | 111 | bind[0].buffer_type = MYSQL_TYPE_LONG; 112 | bind[0].buffer = (void *) &countryId; 113 | bind[0].is_unsigned = 0; 114 | bind[0].is_null = &is_null[0]; 115 | 116 | bind[1].buffer_type = MYSQL_TYPE_STRING; 117 | bind[1].buffer = (void *) countryName; 118 | bind[1].is_null = &is_null[1]; 119 | bind[1].buffer_length = sizeof(countryName); 120 | bind[1].length = &countryNameLen; 121 | 122 | bind[2].buffer_type = MYSQL_TYPE_DATETIME; 123 | bind[2].buffer = (void *) &lastUpdate; 124 | bind[2].is_null = &is_null[2]; 125 | 126 | char thisCountry[STRING_SIZE]; 127 | unsigned long thisCountryLen; 128 | 129 | param[0].buffer_type = MYSQL_TYPE_STRING; 130 | param[0].buffer = (char *)thisCountry; 131 | param[0].buffer_length = STRING_SIZE; 132 | param[0].is_null = 0; 133 | param[0].length = &thisCountryLen; 134 | 135 | if (mysql_stmt_bind_param(stmt, param) != 0) { 136 | print_stmt_error(stmt, "Could not bind SELECT params"); 137 | return 1; 138 | } 139 | 140 | if (mysql_stmt_bind_result(stmt, bind) != 0) { 141 | print_stmt_error(stmt, "Count not bind SELECT results"); 142 | return 1; 143 | } 144 | 145 | strncpy(thisCountry, "Canada", STRING_SIZE); 146 | thisCountryLen = strlen(thisCountry); 147 | 148 | if (mysql_stmt_execute(stmt) != 0) { 149 | print_stmt_error(stmt, "Could not execute SELECT statement"); 150 | return 1; 151 | } 152 | 153 | if (mysql_stmt_store_result(stmt) != 0) { 154 | print_stmt_error(stmt, "Could not execute STORE RESULT"); 155 | return 1; 156 | } 157 | 158 | while (mysql_stmt_fetch(stmt) == 0) { 159 | printf("[%d] ", countryId); 160 | printf("[%.*s] ", (int) countryNameLen, countryName); 161 | printf("[%04d-%02d-%02d %02d:%02d:%02d]", 162 | lastUpdate.year, lastUpdate.month, lastUpdate.day, 163 | lastUpdate.hour, lastUpdate.minute, lastUpdate.second); 164 | printf("\n"); 165 | } 166 | 167 | mysql_stmt_close(stmt); 168 | mysql_close(connect); 169 | 170 | cout << "===> start using objects <===" << endl; 171 | 172 | try 173 | { 174 | Database db("localhost", "root", "", "Sakila", 0, NULL, 0); 175 | db.Connect(); 176 | /* 177 | Statement stmt(db, "select * from country where country = ?"); 178 | stmt.AssignNextParameter(Nullable("USA")); 179 | stmt.Execute(); 180 | stmt.FetchNextRow(); 181 | Nullable countryId = stmt.GetUShortDataInRow(0); 182 | Nullable countryName = stmt.GetStringDataInRow(1); 183 | Nullable lastUpdate = stmt.GetTimeDataInRow(2); 184 | 185 | cout << "Id: " << countryId << " Name: " << countryName << " Last Update: " 186 | << lastUpdate.year << "-" << lastUpdate.month << "-" << lastUpdate.day << " " 187 | << lastUpdate.hour << ":" << lastUpdate.minute << ":" << lastUpdate.second 188 | << endl; 189 | 190 | stmt.ResetParameters(); 191 | stmt.AssignNextParameter(Nullable("Canada")); 192 | stmt.Execute(); 193 | stmt.FetchNextRow(); 194 | countryId = stmt.GetUShortDataInRow(0); 195 | countryName = stmt.GetStringDataInRow(1); 196 | lastUpdate = stmt.GetTimeDataInRow(2); 197 | 198 | cout << "Id: " << countryId << " Name: " << countryName << " Last Update: " 199 | << lastUpdate.year << "-" << lastUpdate.month << "-" << lastUpdate.day << " " 200 | << lastUpdate.hour << ":" << lastUpdate.minute << ":" << lastUpdate.second 201 | << endl; 202 | 203 | Statement insertStmt(db, "insert into country (country) values (?)"); 204 | insertStmt.AssignNextParameter(Nullable("India")); 205 | insertStmt.Execute(); 206 | cout << "Inserted " << insertStmt.NumberOfAffectedRows() << " rows." << endl; 207 | 208 | stmt.ResetParameters(); 209 | stmt.AssignNextParameter(Nullable("India")); 210 | stmt.Execute(); 211 | stmt.FetchNextRow(); 212 | 213 | countryId = stmt.GetUShortDataInRow(0); 214 | countryName = stmt.GetStringDataInRow(1); 215 | lastUpdate = stmt.GetTimeDataInRow(2); 216 | 217 | cout << "Id: " << countryId << " Name: " << countryName << " Last Update: " 218 | << lastUpdate.year << "-" << lastUpdate.month << "-" << lastUpdate.day << " " 219 | << lastUpdate.hour << ":" << lastUpdate.minute << ":" << lastUpdate.second 220 | << endl; 221 | 222 | Statement deleteStmt(db, "delete from country where country_id = ?"); 223 | deleteStmt.AssignNextParameter(Nullable(countryId)); 224 | deleteStmt.Execute(); 225 | cout << "Deleted " << deleteStmt.NumberOfAffectedRows() << " rows." << endl; 226 | */ 227 | 228 | struct stat filestat; 229 | if (stat("pic.jpg", &filestat) != 0) { 230 | cout << "Error 'stat'ing file" << endl; 231 | return 1; 232 | } 233 | 234 | cout << "file size is: " << filestat.st_size << endl; 235 | 236 | ifstream myFile("pic.jpg", ios::in | ios::binary); 237 | myFile.exceptions(std::ios::failbit); 238 | unsigned char picBuf[filestat.st_size]; 239 | myFile.read((char *)picBuf, filestat.st_size); 240 | myFile.close(); 241 | 242 | /* 243 | Binary pic(picBuf, filestat.st_size, filestat.st_size); 244 | Statement staffInsert(db, "update staff set picture = ? where staff_id = 2"); 245 | staffInsert.AssignNextParameter(Nullable(pic)); 246 | staffInsert << execute; 247 | cout << "iUSED SENTINEL updated rows: " << staffInsert.NumberOfAffectedRows() << endl; 248 | */ 249 | 250 | Statement staffSelect(db, "select staff_id, first_name, last_name, picture from staff"); 251 | staffSelect << execute; 252 | staffSelect << fetch; 253 | 254 | cout << "Eof: " << staffSelect.Eof() << endl; 255 | 256 | Nullable staff_id = staffSelect.GetUTinyDataInRow(0); 257 | Nullable staff_first = staffSelect.GetStringDataInRow(1); 258 | Nullable staff_last = staffSelect.GetStringDataInRow(2); 259 | Nullable staff_pic(staffSelect.GetBinaryDataInRow(3)); 260 | 261 | cout << "USED SENTINELS " << "id: " << staff_id << " name: " << staff_first << " " << staff_last << " pic len: " << staff_pic.deref().BufferLength() << endl; 262 | 263 | Statement staff(db, "select picture from staff where first_name = ?"); 264 | staff.AssignNextParameter(Nullable("Mike")); 265 | staff.Execute(); 266 | staff.FetchNextRow(); 267 | Nullable mikepic = staff.GetBinaryDataInRow(0); 268 | 269 | cout << "mikepic is: " << mikepic.deref().BufferLength() << " bytes long" << endl; 270 | 271 | ofstream fs("mike.jpg", ios::out | ios::binary); 272 | fs.exceptions(std::ios::failbit); 273 | fs.write((const char *)mikepic.deref().Buffer(), mikepic.deref().BufferLength()); 274 | fs.close(); 275 | cout << "can you open mike.jpg in a photo editor?" << endl; 276 | 277 | } 278 | catch (DatabaseException &exp) 279 | { 280 | cout << exp << endl; 281 | } 282 | catch(const std::exception &ex) { 283 | cout << "stl failure" << ex.what() << endl; 284 | } 285 | 286 | return 0; 287 | } 288 | -------------------------------------------------------------------------------- /sakila-db/sakila-data.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviDesai/mysqlwrap/214a401a0a53a31d7eefa6ebed52dceb5d083bb1/sakila-db/sakila-data.sql -------------------------------------------------------------------------------- /sakila-db/sakila-schema.sql: -------------------------------------------------------------------------------- 1 | -- Sakila Sample Database Schema 2 | -- Version 0.8 3 | 4 | -- Copyright (c) 2006, MySQL AB 5 | -- All rights reserved. 6 | 7 | -- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 8 | 9 | -- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 10 | -- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 11 | -- * Neither the name of MySQL AB nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 12 | 13 | -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 14 | 15 | 16 | SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0; 17 | SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0; 18 | SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL'; 19 | 20 | DROP SCHEMA IF EXISTS sakila; 21 | CREATE SCHEMA sakila; 22 | USE sakila; 23 | 24 | -- 25 | -- Table structure for table `actor` 26 | -- 27 | 28 | CREATE TABLE actor ( 29 | actor_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, 30 | first_name VARCHAR(45) NOT NULL, 31 | last_name VARCHAR(45) NOT NULL, 32 | last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 33 | PRIMARY KEY (actor_id), 34 | KEY idx_actor_last_name (last_name) 35 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 36 | 37 | -- 38 | -- Table structure for table `address` 39 | -- 40 | 41 | CREATE TABLE address ( 42 | address_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, 43 | address VARCHAR(50) NOT NULL, 44 | address2 VARCHAR(50) DEFAULT NULL, 45 | district VARCHAR(20) NOT NULL, 46 | city_id SMALLINT UNSIGNED NOT NULL, 47 | postal_code VARCHAR(10) DEFAULT NULL, 48 | phone VARCHAR(20) NOT NULL, 49 | last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 50 | PRIMARY KEY (address_id), 51 | KEY idx_fk_city_id (city_id), 52 | CONSTRAINT `fk_address_city` FOREIGN KEY (city_id) REFERENCES city (city_id) ON DELETE RESTRICT ON UPDATE CASCADE 53 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 54 | 55 | -- 56 | -- Table structure for table `category` 57 | -- 58 | 59 | CREATE TABLE category ( 60 | category_id TINYINT UNSIGNED NOT NULL AUTO_INCREMENT, 61 | name VARCHAR(25) NOT NULL, 62 | last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 63 | PRIMARY KEY (category_id) 64 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 65 | 66 | -- 67 | -- Table structure for table `city` 68 | -- 69 | 70 | CREATE TABLE city ( 71 | city_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, 72 | city VARCHAR(50) NOT NULL, 73 | country_id SMALLINT UNSIGNED NOT NULL, 74 | last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 75 | PRIMARY KEY (city_id), 76 | KEY idx_fk_country_id (country_id), 77 | CONSTRAINT `fk_city_country` FOREIGN KEY (country_id) REFERENCES country (country_id) ON DELETE RESTRICT ON UPDATE CASCADE 78 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 79 | 80 | -- 81 | -- Table structure for table `country` 82 | -- 83 | 84 | CREATE TABLE country ( 85 | country_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, 86 | country VARCHAR(50) NOT NULL, 87 | last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 88 | PRIMARY KEY (country_id) 89 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 90 | 91 | -- 92 | -- Table structure for table `customer` 93 | -- 94 | 95 | CREATE TABLE customer ( 96 | customer_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, 97 | store_id TINYINT UNSIGNED NOT NULL, 98 | first_name VARCHAR(45) NOT NULL, 99 | last_name VARCHAR(45) NOT NULL, 100 | email VARCHAR(50) DEFAULT NULL, 101 | address_id SMALLINT UNSIGNED NOT NULL, 102 | active BOOLEAN NOT NULL DEFAULT TRUE, 103 | create_date DATETIME NOT NULL, 104 | last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 105 | PRIMARY KEY (customer_id), 106 | KEY idx_fk_store_id (store_id), 107 | KEY idx_fk_address_id (address_id), 108 | KEY idx_last_name (last_name), 109 | CONSTRAINT fk_customer_address FOREIGN KEY (address_id) REFERENCES address (address_id) ON DELETE RESTRICT ON UPDATE CASCADE, 110 | CONSTRAINT fk_customer_store FOREIGN KEY (store_id) REFERENCES store (store_id) ON DELETE RESTRICT ON UPDATE CASCADE 111 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 112 | 113 | -- 114 | -- Table structure for table `film` 115 | -- 116 | 117 | CREATE TABLE film ( 118 | film_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, 119 | title VARCHAR(255) NOT NULL, 120 | description TEXT DEFAULT NULL, 121 | release_year YEAR DEFAULT NULL, 122 | language_id TINYINT UNSIGNED NOT NULL, 123 | original_language_id TINYINT UNSIGNED DEFAULT NULL, 124 | rental_duration TINYINT UNSIGNED NOT NULL DEFAULT 3, 125 | rental_rate DECIMAL(4,2) NOT NULL DEFAULT 4.99, 126 | length SMALLINT UNSIGNED DEFAULT NULL, 127 | replacement_cost DECIMAL(5,2) NOT NULL DEFAULT 19.99, 128 | rating ENUM('G','PG','PG-13','R','NC-17') DEFAULT 'G', 129 | special_features SET('Trailers','Commentaries','Deleted Scenes','Behind the Scenes') DEFAULT NULL, 130 | last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 131 | PRIMARY KEY (film_id), 132 | KEY idx_title (title), 133 | KEY idx_fk_language_id (language_id), 134 | KEY idx_fk_original_language_id (original_language_id), 135 | CONSTRAINT fk_film_language FOREIGN KEY (language_id) REFERENCES language (language_id) ON DELETE RESTRICT ON UPDATE CASCADE, 136 | CONSTRAINT fk_film_language_original FOREIGN KEY (original_language_id) REFERENCES language (language_id) ON DELETE RESTRICT ON UPDATE CASCADE 137 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 138 | 139 | -- 140 | -- Table structure for table `film_actor` 141 | -- 142 | 143 | CREATE TABLE film_actor ( 144 | actor_id SMALLINT UNSIGNED NOT NULL, 145 | film_id SMALLINT UNSIGNED NOT NULL, 146 | last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 147 | PRIMARY KEY (actor_id,film_id), 148 | KEY idx_fk_film_id (`film_id`), 149 | CONSTRAINT fk_film_actor_actor FOREIGN KEY (actor_id) REFERENCES actor (actor_id) ON DELETE RESTRICT ON UPDATE CASCADE, 150 | CONSTRAINT fk_film_actor_film FOREIGN KEY (film_id) REFERENCES film (film_id) ON DELETE RESTRICT ON UPDATE CASCADE 151 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 152 | 153 | -- 154 | -- Table structure for table `film_category` 155 | -- 156 | 157 | CREATE TABLE film_category ( 158 | film_id SMALLINT UNSIGNED NOT NULL, 159 | category_id TINYINT UNSIGNED NOT NULL, 160 | last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 161 | PRIMARY KEY (film_id, category_id), 162 | CONSTRAINT fk_film_category_film FOREIGN KEY (film_id) REFERENCES film (film_id) ON DELETE RESTRICT ON UPDATE CASCADE, 163 | CONSTRAINT fk_film_category_category FOREIGN KEY (category_id) REFERENCES category (category_id) ON DELETE RESTRICT ON UPDATE CASCADE 164 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 165 | 166 | -- 167 | -- Table structure for table `film_text` 168 | -- 169 | 170 | CREATE TABLE film_text ( 171 | film_id SMALLINT NOT NULL, 172 | title VARCHAR(255) NOT NULL, 173 | description TEXT, 174 | PRIMARY KEY (film_id), 175 | FULLTEXT KEY idx_title_description (title,description) 176 | )ENGINE=MyISAM DEFAULT CHARSET=utf8; 177 | 178 | -- 179 | -- Triggers for loading film_text from film 180 | -- 181 | 182 | DELIMITER ;; 183 | CREATE TRIGGER `ins_film` AFTER INSERT ON `film` FOR EACH ROW BEGIN 184 | INSERT INTO film_text (film_id, title, description) 185 | VALUES (new.film_id, new.title, new.description); 186 | END;; 187 | 188 | 189 | CREATE TRIGGER `upd_film` AFTER UPDATE ON `film` FOR EACH ROW BEGIN 190 | IF (old.title != new.title) OR (old.description != new.description) OR (old.film_id != new.film_id) 191 | THEN 192 | UPDATE film_text 193 | SET title=new.title, 194 | description=new.description, 195 | film_id=new.film_id 196 | WHERE film_id=old.film_id; 197 | END IF; 198 | END;; 199 | 200 | 201 | CREATE TRIGGER `del_film` AFTER DELETE ON `film` FOR EACH ROW BEGIN 202 | DELETE FROM film_text WHERE film_id = old.film_id; 203 | END;; 204 | 205 | DELIMITER ; 206 | 207 | -- 208 | -- Table structure for table `inventory` 209 | -- 210 | 211 | CREATE TABLE inventory ( 212 | inventory_id MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, 213 | film_id SMALLINT UNSIGNED NOT NULL, 214 | store_id TINYINT UNSIGNED NOT NULL, 215 | last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 216 | PRIMARY KEY (inventory_id), 217 | KEY idx_fk_film_id (film_id), 218 | KEY idx_store_id_film_id (store_id,film_id), 219 | CONSTRAINT fk_inventory_store FOREIGN KEY (store_id) REFERENCES store (store_id) ON DELETE RESTRICT ON UPDATE CASCADE, 220 | CONSTRAINT fk_inventory_film FOREIGN KEY (film_id) REFERENCES film (film_id) ON DELETE RESTRICT ON UPDATE CASCADE 221 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 222 | 223 | -- 224 | -- Table structure for table `language` 225 | -- 226 | 227 | CREATE TABLE language ( 228 | language_id TINYINT UNSIGNED NOT NULL AUTO_INCREMENT, 229 | name CHAR(20) NOT NULL, 230 | last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 231 | PRIMARY KEY (language_id) 232 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 233 | 234 | -- 235 | -- Table structure for table `payment` 236 | -- 237 | 238 | CREATE TABLE payment ( 239 | payment_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, 240 | customer_id SMALLINT UNSIGNED NOT NULL, 241 | staff_id TINYINT UNSIGNED NOT NULL, 242 | rental_id INT DEFAULT NULL, 243 | amount DECIMAL(5,2) NOT NULL, 244 | payment_date DATETIME NOT NULL, 245 | last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 246 | PRIMARY KEY (payment_id), 247 | KEY idx_fk_staff_id (staff_id), 248 | KEY idx_fk_customer_id (customer_id), 249 | CONSTRAINT fk_payment_rental FOREIGN KEY (rental_id) REFERENCES rental (rental_id) ON DELETE SET NULL ON UPDATE CASCADE, 250 | CONSTRAINT fk_payment_customer FOREIGN KEY (customer_id) REFERENCES customer (customer_id) ON DELETE RESTRICT ON UPDATE CASCADE, 251 | CONSTRAINT fk_payment_staff FOREIGN KEY (staff_id) REFERENCES staff (staff_id) ON DELETE RESTRICT ON UPDATE CASCADE 252 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 253 | 254 | 255 | -- 256 | -- Table structure for table `rental` 257 | -- 258 | 259 | CREATE TABLE rental ( 260 | rental_id INT NOT NULL AUTO_INCREMENT, 261 | rental_date DATETIME NOT NULL, 262 | inventory_id MEDIUMINT UNSIGNED NOT NULL, 263 | customer_id SMALLINT UNSIGNED NOT NULL, 264 | return_date DATETIME DEFAULT NULL, 265 | staff_id TINYINT UNSIGNED NOT NULL, 266 | last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 267 | PRIMARY KEY (rental_id), 268 | UNIQUE KEY (rental_date,inventory_id,customer_id), 269 | KEY idx_fk_inventory_id (inventory_id), 270 | KEY idx_fk_customer_id (customer_id), 271 | KEY idx_fk_staff_id (staff_id), 272 | CONSTRAINT fk_rental_staff FOREIGN KEY (staff_id) REFERENCES staff (staff_id) ON DELETE RESTRICT ON UPDATE CASCADE, 273 | CONSTRAINT fk_rental_inventory FOREIGN KEY (inventory_id) REFERENCES inventory (inventory_id) ON DELETE RESTRICT ON UPDATE CASCADE, 274 | CONSTRAINT fk_rental_customer FOREIGN KEY (customer_id) REFERENCES customer (customer_id) ON DELETE RESTRICT ON UPDATE CASCADE 275 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 276 | 277 | -- 278 | -- Table structure for table `staff` 279 | -- 280 | 281 | CREATE TABLE staff ( 282 | staff_id TINYINT UNSIGNED NOT NULL AUTO_INCREMENT, 283 | first_name VARCHAR(45) NOT NULL, 284 | last_name VARCHAR(45) NOT NULL, 285 | address_id SMALLINT UNSIGNED NOT NULL, 286 | picture BLOB DEFAULT NULL, 287 | email VARCHAR(50) DEFAULT NULL, 288 | store_id TINYINT UNSIGNED NOT NULL, 289 | active BOOLEAN NOT NULL DEFAULT TRUE, 290 | username VARCHAR(16) NOT NULL, 291 | password VARCHAR(40) BINARY DEFAULT NULL, 292 | last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 293 | PRIMARY KEY (staff_id), 294 | KEY idx_fk_store_id (store_id), 295 | KEY idx_fk_address_id (address_id), 296 | CONSTRAINT fk_staff_store FOREIGN KEY (store_id) REFERENCES store (store_id) ON DELETE RESTRICT ON UPDATE CASCADE, 297 | CONSTRAINT fk_staff_address FOREIGN KEY (address_id) REFERENCES address (address_id) ON DELETE RESTRICT ON UPDATE CASCADE 298 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 299 | 300 | -- 301 | -- Table structure for table `store` 302 | -- 303 | 304 | CREATE TABLE store ( 305 | store_id TINYINT UNSIGNED NOT NULL AUTO_INCREMENT, 306 | manager_staff_id TINYINT UNSIGNED NOT NULL, 307 | address_id SMALLINT UNSIGNED NOT NULL, 308 | last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 309 | PRIMARY KEY (store_id), 310 | UNIQUE KEY idx_unique_manager (manager_staff_id), 311 | KEY idx_fk_address_id (address_id), 312 | CONSTRAINT fk_store_staff FOREIGN KEY (manager_staff_id) REFERENCES staff (staff_id) ON DELETE RESTRICT ON UPDATE CASCADE, 313 | CONSTRAINT fk_store_address FOREIGN KEY (address_id) REFERENCES address (address_id) ON DELETE RESTRICT ON UPDATE CASCADE 314 | )ENGINE=InnoDB DEFAULT CHARSET=utf8; 315 | 316 | -- 317 | -- View structure for view `customer_list` 318 | -- 319 | 320 | CREATE VIEW customer_list 321 | AS 322 | SELECT cu.customer_id AS ID, CONCAT(cu.first_name, _utf8' ', cu.last_name) AS name, a.address AS address, a.postal_code AS `zip code`, 323 | a.phone AS phone, city.city AS city, country.country AS country, IF(cu.active, _utf8'active',_utf8'') AS notes, cu.store_id AS SID 324 | FROM customer AS cu JOIN address AS a ON cu.address_id = a.address_id JOIN city ON a.city_id = city.city_id 325 | JOIN country ON city.country_id = country.country_id; 326 | 327 | -- 328 | -- View structure for view `film_list` 329 | -- 330 | 331 | CREATE VIEW film_list 332 | AS 333 | SELECT film.film_id AS FID, film.title AS title, film.description AS description, category.name AS category, film.rental_rate AS price, 334 | film.length AS length, film.rating AS rating, GROUP_CONCAT(CONCAT(actor.first_name, _utf8' ', actor.last_name) SEPARATOR ', ') AS actors 335 | FROM category LEFT JOIN film_category ON category.category_id = film_category.category_id LEFT JOIN film ON film_category.film_id = film.film_id 336 | JOIN film_actor ON film.film_id = film_actor.film_id 337 | JOIN actor ON film_actor.actor_id = actor.actor_id 338 | GROUP BY film.film_id; 339 | 340 | -- 341 | -- View structure for view `nicer_but_slower_film_list` 342 | -- 343 | 344 | CREATE VIEW nicer_but_slower_film_list 345 | AS 346 | SELECT film.film_id AS FID, film.title AS title, film.description AS description, category.name AS category, film.rental_rate AS price, 347 | film.length AS length, film.rating AS rating, GROUP_CONCAT(CONCAT(CONCAT(UCASE(SUBSTR(actor.first_name,1,1)), 348 | LCASE(SUBSTR(actor.first_name,2,LENGTH(actor.first_name))),_utf8' ',CONCAT(UCASE(SUBSTR(actor.last_name,1,1)), 349 | LCASE(SUBSTR(actor.last_name,2,LENGTH(actor.last_name)))))) SEPARATOR ', ') AS actors 350 | FROM category LEFT JOIN film_category ON category.category_id = film_category.category_id LEFT JOIN film ON film_category.film_id = film.film_id 351 | JOIN film_actor ON film.film_id = film_actor.film_id 352 | JOIN actor ON film_actor.actor_id = actor.actor_id 353 | GROUP BY film.film_id; 354 | 355 | -- 356 | -- View structure for view `staff_list` 357 | -- 358 | 359 | CREATE VIEW staff_list 360 | AS 361 | SELECT s.staff_id AS ID, CONCAT(s.first_name, _utf8' ', s.last_name) AS name, a.address AS address, a.postal_code AS `zip code`, a.phone AS phone, 362 | city.city AS city, country.country AS country, s.store_id AS SID 363 | FROM staff AS s JOIN address AS a ON s.address_id = a.address_id JOIN city ON a.city_id = city.city_id 364 | JOIN country ON city.country_id = country.country_id; 365 | 366 | -- 367 | -- View structure for view `sales_by_store` 368 | -- 369 | 370 | CREATE VIEW sales_by_store 371 | AS 372 | SELECT 373 | CONCAT(c.city, _utf8',', cy.country) AS store 374 | , CONCAT(m.first_name, _utf8' ', m.last_name) AS manager 375 | , SUM(p.amount) AS total_sales 376 | FROM payment AS p 377 | INNER JOIN rental AS r ON p.rental_id = r.rental_id 378 | INNER JOIN inventory AS i ON r.inventory_id = i.inventory_id 379 | INNER JOIN store AS s ON i.store_id = s.store_id 380 | INNER JOIN address AS a ON s.address_id = a.address_id 381 | INNER JOIN city AS c ON a.city_id = c.city_id 382 | INNER JOIN country AS cy ON c.country_id = cy.country_id 383 | INNER JOIN staff AS m ON s.manager_staff_id = m.staff_id 384 | GROUP BY s.store_id 385 | ORDER BY cy.country, c.city; 386 | 387 | -- 388 | -- View structure for view `sales_by_film_category` 389 | -- 390 | -- Note that total sales will add up to >100% because 391 | -- some titles belong to more than 1 category 392 | -- 393 | 394 | CREATE VIEW sales_by_film_category 395 | AS 396 | SELECT 397 | c.name AS category 398 | , SUM(p.amount) AS total_sales 399 | FROM payment AS p 400 | INNER JOIN rental AS r ON p.rental_id = r.rental_id 401 | INNER JOIN inventory AS i ON r.inventory_id = i.inventory_id 402 | INNER JOIN film AS f ON i.film_id = f.film_id 403 | INNER JOIN film_category AS fc ON f.film_id = fc.film_id 404 | INNER JOIN category AS c ON fc.category_id = c.category_id 405 | GROUP BY c.name 406 | ORDER BY total_sales DESC; 407 | 408 | -- 409 | -- View structure for view `actor_info` 410 | -- 411 | 412 | CREATE DEFINER=CURRENT_USER SQL SECURITY INVOKER VIEW actor_info 413 | AS 414 | SELECT 415 | a.actor_id, 416 | a.first_name, 417 | a.last_name, 418 | GROUP_CONCAT(DISTINCT CONCAT(c.name, ': ', 419 | (SELECT GROUP_CONCAT(f.title ORDER BY f.title SEPARATOR ', ') 420 | FROM sakila.film f 421 | INNER JOIN sakila.film_category fc 422 | ON f.film_id = fc.film_id 423 | INNER JOIN sakila.film_actor fa 424 | ON f.film_id = fa.film_id 425 | WHERE fc.category_id = c.category_id 426 | AND fa.actor_id = a.actor_id 427 | ) 428 | ) 429 | ORDER BY c.name SEPARATOR '; ') 430 | AS film_info 431 | FROM sakila.actor a 432 | LEFT JOIN sakila.film_actor fa 433 | ON a.actor_id = fa.actor_id 434 | LEFT JOIN sakila.film_category fc 435 | ON fa.film_id = fc.film_id 436 | LEFT JOIN sakila.category c 437 | ON fc.category_id = c.category_id 438 | GROUP BY a.actor_id, a.first_name, a.last_name; 439 | 440 | -- 441 | -- Procedure structure for procedure `rewards_report` 442 | -- 443 | 444 | DELIMITER // 445 | 446 | CREATE PROCEDURE rewards_report ( 447 | IN min_monthly_purchases TINYINT UNSIGNED 448 | , IN min_dollar_amount_purchased DECIMAL(10,2) UNSIGNED 449 | , OUT count_rewardees INT 450 | ) 451 | LANGUAGE SQL 452 | NOT DETERMINISTIC 453 | READS SQL DATA 454 | SQL SECURITY DEFINER 455 | COMMENT 'Provides a customizable report on best customers' 456 | proc: BEGIN 457 | 458 | DECLARE last_month_start DATE; 459 | DECLARE last_month_end DATE; 460 | 461 | /* Some sanity checks... */ 462 | IF min_monthly_purchases = 0 THEN 463 | SELECT 'Minimum monthly purchases parameter must be > 0'; 464 | LEAVE proc; 465 | END IF; 466 | IF min_dollar_amount_purchased = 0.00 THEN 467 | SELECT 'Minimum monthly dollar amount purchased parameter must be > $0.00'; 468 | LEAVE proc; 469 | END IF; 470 | 471 | /* Determine start and end time periods */ 472 | SET last_month_start = DATE_SUB(CURRENT_DATE(), INTERVAL 1 MONTH); 473 | SET last_month_start = STR_TO_DATE(CONCAT(YEAR(last_month_start),'-',MONTH(last_month_start),'-01'),'%Y-%m-%d'); 474 | SET last_month_end = LAST_DAY(last_month_start); 475 | 476 | /* 477 | Create a temporary storage area for 478 | Customer IDs. 479 | */ 480 | CREATE TEMPORARY TABLE tmpCustomer (customer_id SMALLINT UNSIGNED NOT NULL PRIMARY KEY); 481 | 482 | /* 483 | Find all customers meeting the 484 | monthly purchase requirements 485 | */ 486 | INSERT INTO tmpCustomer (customer_id) 487 | SELECT p.customer_id 488 | FROM payment AS p 489 | WHERE DATE(p.payment_date) BETWEEN last_month_start AND last_month_end 490 | GROUP BY customer_id 491 | HAVING SUM(p.amount) > min_dollar_amount_purchased 492 | AND COUNT(customer_id) > min_monthly_purchases; 493 | 494 | /* Populate OUT parameter with count of found customers */ 495 | SELECT COUNT(*) FROM tmpCustomer INTO count_rewardees; 496 | 497 | /* 498 | Output ALL customer information of matching rewardees. 499 | Customize output as needed. 500 | */ 501 | SELECT c.* 502 | FROM tmpCustomer AS t 503 | INNER JOIN customer AS c ON t.customer_id = c.customer_id; 504 | 505 | /* Clean up */ 506 | DROP TABLE tmpCustomer; 507 | END // 508 | 509 | DELIMITER ; 510 | 511 | DELIMITER $$ 512 | 513 | CREATE FUNCTION get_customer_balance(p_customer_id INT, p_effective_date DATETIME) RETURNS DECIMAL(5,2) 514 | DETERMINISTIC 515 | READS SQL DATA 516 | BEGIN 517 | 518 | #OK, WE NEED TO CALCULATE THE CURRENT BALANCE GIVEN A CUSTOMER_ID AND A DATE 519 | #THAT WE WANT THE BALANCE TO BE EFFECTIVE FOR. THE BALANCE IS: 520 | # 1) RENTAL FEES FOR ALL PREVIOUS RENTALS 521 | # 2) ONE DOLLAR FOR EVERY DAY THE PREVIOUS RENTALS ARE OVERDUE 522 | # 3) IF A FILM IS MORE THAN RENTAL_DURATION * 2 OVERDUE, CHARGE THE REPLACEMENT_COST 523 | # 4) SUBTRACT ALL PAYMENTS MADE BEFORE THE DATE SPECIFIED 524 | 525 | DECLARE v_rentfees DECIMAL(5,2); #FEES PAID TO RENT THE VIDEOS INITIALLY 526 | DECLARE v_overfees INTEGER; #LATE FEES FOR PRIOR RENTALS 527 | DECLARE v_payments DECIMAL(5,2); #SUM OF PAYMENTS MADE PREVIOUSLY 528 | 529 | SELECT IFNULL(SUM(film.rental_rate),0) INTO v_rentfees 530 | FROM film, inventory, rental 531 | WHERE film.film_id = inventory.film_id 532 | AND inventory.inventory_id = rental.inventory_id 533 | AND rental.rental_date <= p_effective_date 534 | AND rental.customer_id = p_customer_id; 535 | 536 | SELECT IFNULL(SUM(IF((TO_DAYS(rental.return_date) - TO_DAYS(rental.rental_date)) > film.rental_duration, 537 | ((TO_DAYS(rental.return_date) - TO_DAYS(rental.rental_date)) - film.rental_duration),0)),0) INTO v_overfees 538 | FROM rental, inventory, film 539 | WHERE film.film_id = inventory.film_id 540 | AND inventory.inventory_id = rental.inventory_id 541 | AND rental.rental_date <= p_effective_date 542 | AND rental.customer_id = p_customer_id; 543 | 544 | 545 | SELECT IFNULL(SUM(payment.amount),0) INTO v_payments 546 | FROM payment 547 | 548 | WHERE payment.payment_date <= p_effective_date 549 | AND payment.customer_id = p_customer_id; 550 | 551 | RETURN v_rentfees + v_overfees - v_payments; 552 | END $$ 553 | 554 | DELIMITER ; 555 | 556 | DELIMITER $$ 557 | 558 | CREATE PROCEDURE film_in_stock(IN p_film_id INT, IN p_store_id INT, OUT p_film_count INT) 559 | READS SQL DATA 560 | BEGIN 561 | SELECT inventory_id 562 | FROM inventory 563 | WHERE film_id = p_film_id 564 | AND store_id = p_store_id 565 | AND inventory_in_stock(inventory_id); 566 | 567 | SELECT FOUND_ROWS() INTO p_film_count; 568 | END $$ 569 | 570 | DELIMITER ; 571 | 572 | DELIMITER $$ 573 | 574 | CREATE PROCEDURE film_not_in_stock(IN p_film_id INT, IN p_store_id INT, OUT p_film_count INT) 575 | READS SQL DATA 576 | BEGIN 577 | SELECT inventory_id 578 | FROM inventory 579 | WHERE film_id = p_film_id 580 | AND store_id = p_store_id 581 | AND NOT inventory_in_stock(inventory_id); 582 | 583 | SELECT FOUND_ROWS() INTO p_film_count; 584 | END $$ 585 | 586 | DELIMITER ; 587 | 588 | DELIMITER $$ 589 | 590 | CREATE FUNCTION inventory_held_by_customer(p_inventory_id INT) RETURNS INT 591 | READS SQL DATA 592 | BEGIN 593 | DECLARE v_customer_id INT; 594 | DECLARE EXIT HANDLER FOR NOT FOUND RETURN NULL; 595 | 596 | SELECT customer_id INTO v_customer_id 597 | FROM rental 598 | WHERE return_date IS NULL 599 | AND inventory_id = p_inventory_id; 600 | 601 | RETURN v_customer_id; 602 | END $$ 603 | 604 | DELIMITER ; 605 | 606 | DELIMITER $$ 607 | 608 | CREATE FUNCTION inventory_in_stock(p_inventory_id INT) RETURNS BOOLEAN 609 | READS SQL DATA 610 | BEGIN 611 | DECLARE v_rentals INT; 612 | DECLARE v_out INT; 613 | 614 | #AN ITEM IS IN-STOCK IF THERE ARE EITHER NO ROWS IN THE rental TABLE 615 | #FOR THE ITEM OR ALL ROWS HAVE return_date POPULATED 616 | 617 | SELECT COUNT(*) INTO v_rentals 618 | FROM rental 619 | WHERE inventory_id = p_inventory_id; 620 | 621 | IF v_rentals = 0 THEN 622 | RETURN TRUE; 623 | END IF; 624 | 625 | SELECT COUNT(rental_id) INTO v_out 626 | FROM inventory LEFT JOIN rental USING(inventory_id) 627 | WHERE inventory.inventory_id = p_inventory_id 628 | AND rental.return_date IS NULL; 629 | 630 | IF v_out > 0 THEN 631 | RETURN FALSE; 632 | ELSE 633 | RETURN TRUE; 634 | END IF; 635 | END $$ 636 | 637 | DELIMITER ; 638 | 639 | SET SQL_MODE=@OLD_SQL_MODE; 640 | SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; 641 | SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; 642 | 643 | 644 | -------------------------------------------------------------------------------- /sakila-db/sakila.mwb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RaviDesai/mysqlwrap/214a401a0a53a31d7eefa6ebed52dceb5d083bb1/sakila-db/sakila.mwb -------------------------------------------------------------------------------- /test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "UTFail.h" 4 | 5 | #include "TestNullable.h" 6 | #include "TestBinary.h" 7 | #include "TestDatabaseException.h" 8 | #include "TestDatabase.h" 9 | #include "TestImport.h" 10 | #include "TestJulian.h" 11 | #include "TestAdhocParameter.h" 12 | 13 | using namespace std; 14 | using namespace MySQLWrap; 15 | 16 | int main(int argc, char** argv) 17 | { 18 | cout << "Begin Test Suite" << endl; 19 | bool embedded = false; 20 | cout << "embedded: " << embedded << endl; 21 | 22 | LibraryInitializer di(embedded); 23 | ThreadInitializer ti; 24 | 25 | int failures = 0; 26 | TestImport ts0; 27 | failures += ts0.RunTests(embedded); 28 | 29 | TestNullable ts1; 30 | failures += ts1.RunTests(); 31 | 32 | TestBinary ts2; 33 | failures += ts2.RunTests(); 34 | 35 | TestDatabaseException ts3; 36 | failures += ts3.RunTests(embedded); 37 | 38 | TestDatabase ts4; 39 | failures += ts4.RunTests(embedded); 40 | 41 | TestJulian ts5; 42 | failures += ts5.RunTests(); 43 | 44 | TestAdhocParameter ts6; 45 | failures += ts6.RunTests(); 46 | 47 | cout << "End Test Suite" << endl; 48 | 49 | return failures; 50 | } 51 | -------------------------------------------------------------------------------- /testemb.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "UTFail.h" 4 | 5 | #include "TestNullable.h" 6 | #include "TestBinary.h" 7 | #include "TestDatabaseException.h" 8 | #include "TestDatabase.h" 9 | #include "TestImport.h" 10 | 11 | using namespace std; 12 | using namespace MySQLWrap; 13 | 14 | int main(int argc, char** argv) 15 | { 16 | cout << "Begin Test Suite" << endl; 17 | bool embedded = true; 18 | cout << "embedded: " << embedded << endl; 19 | 20 | LibraryInitializer di(embedded); 21 | ThreadInitializer ti; 22 | 23 | int failures = 0; 24 | TestImport ts0; 25 | failures += ts0.RunTests(embedded); 26 | 27 | TestNullable ts1; 28 | failures += ts1.RunTests(); 29 | 30 | TestBinary ts2; 31 | failures += ts2.RunTests(); 32 | 33 | TestDatabaseException ts3; 34 | failures += ts3.RunTests(embedded); 35 | 36 | TestDatabase ts4; 37 | failures += ts4.RunTests(embedded); 38 | 39 | cout << "End Test Suite" << endl; 40 | 41 | return failures; 42 | } 43 | --------------------------------------------------------------------------------