#ifndef __DatabaseSupport_h_ #define __DatabaseSupport_h_ #include #include #include #include #include "../shared/MiscSupport.h" inline std::string mysqlEscapeString(std::string const &s) { char fixedBuffer[1000]; char *buffer = fixedBuffer; const long unsigned bufferLength = s.length() * 2 + 1; if (bufferLength > 1000) { buffer = new char[bufferLength]; } long unsigned newLength = mysql_escape_string(buffer, s.data(), s.length()); std::string result(buffer, newLength); if (buffer != fixedBuffer) { delete[] buffer; } return result; } class MysqlResult : NoCopy, NoAssign { private: MYSQL_RES *_result; MYSQL_ROW _row; unsigned long *_lengths; MYSQL_FIELD *_fields; std::map< std::string, int > _fieldsByName; my_ulonglong _affectedRows; int _fieldCount; public: MysqlResult(MYSQL *connection) { _result = mysql_store_result(connection); if (_result) { _affectedRows = mysql_affected_rows(connection); _fieldCount = mysql_field_count(connection); _fields = mysql_fetch_fields(_result); for (int i = 0; i < _fieldCount; i++) { _fieldsByName[_fields[i].name] = i; } _row = mysql_fetch_row(_result); _lengths = mysql_fetch_lengths(_result); } else { _affectedRows = 0; _fieldCount = 0; _fields = NULL; _row = NULL; _lengths = NULL; } // We don't allow multiple statements. However, a call to a stored // procedure looks like multiple statements, even if it's only returning // one result set. For simplicity we just throw away any result sets // aside from the first one. If we don't do this, we will be disconnected // by the server on the first call after a stored procedure. while (mysql_next_result(connection)==0) mysql_free_result(mysql_use_result(connection)); } ~MysqlResult() { if (_result) { mysql_free_result(_result); } } int numRows() const { if (_result) { return mysql_num_rows(_result); } else { return 0; } } bool rowIsValid() const { return _row; } void nextRow() { if (_row) { _row = mysql_fetch_row(_result); _lengths = mysql_fetch_lengths(_result); } } bool fieldIsEmpty(int column) const { return (!_row) // Past the last row. || (column < 0) // Invalid column, possibly a bad column name. || (column >= _fieldCount) // Invalid column || (!_row[column]); // Value is null } bool fieldIsEmpty(std::string key) const { return fieldIsEmpty(getProperty(_fieldsByName, key, -1)); } long getIntegerField(int column, long defaultValue) const { if (fieldIsEmpty(column)) { return defaultValue; } else { return strtolDefault(_row[column], defaultValue); } // What about a string that contains a valid number followed by // a null which is part of the string? We will not report an // error in that case, but we should. } long getIntegerField(std::string key, long defaultValue) const { return getIntegerField(getProperty(_fieldsByName, key, -1), defaultValue); } std::string getStringField(int column, std::string defaultValue = "") const { if (fieldIsEmpty(column)) { return defaultValue; } return std::string(_row[column], _lengths[column]); } std::string getStringField(std::string key, std::string defaultValue = "") const { return getStringField(getProperty(_fieldsByName, key, -1), defaultValue); } void getStringField(std::string key, std::string &result, bool &found) const { // You could get the same functionality by calling fieldIsValid() followed // by getStringField(). But that would cause two lookups in the // _fieldsByName table, and some additional error checking. This is more // effecient. int column = getProperty(_fieldsByName, key, -1); found = !fieldIsEmpty(column); if (found) { result = std::string(_row[column], _lengths[column]); } } double getDoubleField(int column, double defaultValue) const { if (fieldIsEmpty(column)) { return defaultValue; } else { return strtodDefault(_row[column], defaultValue); } // What about a string that contains a valid number followed by // a null which is part of the string? We will not report an // error in that case, but we should. } double getDoubleField(std::string key, double defaultValue) const { return getDoubleField(getProperty(_fieldsByName, key, -1), defaultValue); } bool getBooleanField(std::string key) const { // This is consitent with PHP and helps us translate PHP code to C++. const std::string value = getStringField(key); return (value != "") && (value != "0"); } bool getBooleanField(int column) const { const std::string value = getStringField(column); return (value != "") && (value != "0"); } // This never seems to work right for me. See UserInfo.C and MiscNFS.C for // examples. Instead, add SELECT ROW_COUNT() after the query you want to know // about. The mysql command line client seems to work, so maybe there's a // trick to this and it can be fixed. my_ulonglong getAffectedRows() const { // This works best with CLIENT_FOUND_ROWS // http://dev.mysql.com/doc/mysql/en/mysql_affected_rows.html return _affectedRows; } void getColumnNames(std::vector< std::string > &dest) const { dest.clear(); dest.reserve(_fieldCount); for (int i = 0; i < _fieldCount; i++) dest.push_back(_fields[i].name); } int getFieldCount() const { return _fieldCount; } }; // Because MysqlResult owns memory that it needs to dispose of, // it is often convenient to use a reference counted version of it. typedef RefCount< MysqlResult > MysqlResultRef; // The underlying function don't seem to be defined in the mysql.h on ben // doucette. They are defined on turtle. inline void mysqlThreadInit() {} inline void mysqlThreadEnd() {} #endif