PreparedResultSet::PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES *result, uint64 rowCount, uint32 fieldCount) : m_rowCount(rowCount), m_rowPosition(0), m_fieldCount(fieldCount), m_rBind(NULL), m_stmt(stmt), m_res(result), m_isNull(NULL), m_length(NULL) { if (!m_res) return; if (m_stmt->bind_result_done) { delete[] m_stmt->bind->length; delete[] m_stmt->bind->is_null; } m_rBind = new MYSQL_BIND[m_fieldCount]; m_isNull = new my_bool[m_fieldCount]; m_length = new unsigned long[m_fieldCount]; memset(m_isNull, 0, sizeof(my_bool) * m_fieldCount); memset(m_rBind, 0, sizeof(MYSQL_BIND) * m_fieldCount); memset(m_length, 0, sizeof(unsigned long) * m_fieldCount); //- This is where we store the (entire) resultset if (mysql_stmt_store_result(m_stmt)) { TC_LOG_WARN("sql.sql", "%s:mysql_stmt_store_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt)); delete[] m_rBind; delete[] m_isNull; delete[] m_length; return; } //- This is where we prepare the buffer based on metadata uint32 i = 0; MYSQL_FIELD* field = mysql_fetch_field(m_res); while (field) { size_t size = Field::SizeForType(field); m_rBind[i].buffer_type = field->type; m_rBind[i].buffer = malloc(size); memset(m_rBind[i].buffer, 0, size); m_rBind[i].buffer_length = size; m_rBind[i].length = &m_length[i]; m_rBind[i].is_null = &m_isNull[i]; m_rBind[i].error = NULL; m_rBind[i].is_unsigned = field->flags & UNSIGNED_FLAG; ++i; field = mysql_fetch_field(m_res); } //- This is where we bind the bind the buffer to the statement if (mysql_stmt_bind_result(m_stmt, m_rBind)) { TC_LOG_WARN("sql.sql", "%s:mysql_stmt_bind_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt)); delete[] m_rBind; delete[] m_isNull; delete[] m_length; return; } m_rowCount = mysql_stmt_num_rows(m_stmt); m_rows.resize(uint32(m_rowCount)); while (_NextRow()) { m_rows[uint32(m_rowPosition)] = new Field[m_fieldCount]; for (uint64 fIndex = 0; fIndex < m_fieldCount; ++fIndex) { if (!*m_rBind[fIndex].is_null) m_rows[uint32(m_rowPosition)][fIndex].SetByteValue(m_rBind[fIndex].buffer, m_rBind[fIndex].buffer_length, m_rBind[fIndex].buffer_type, *m_rBind[fIndex].length); else switch (m_rBind[fIndex].buffer_type) { case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: m_rows[uint32(m_rowPosition)][fIndex].SetByteValue("", m_rBind[fIndex].buffer_length, m_rBind[fIndex].buffer_type, *m_rBind[fIndex].length); break; default: m_rows[uint32(m_rowPosition)][fIndex].SetByteValue(nullptr, m_rBind[fIndex].buffer_length, m_rBind[fIndex].buffer_type, *m_rBind[fIndex].length); } } m_rowPosition++; } m_rowPosition = 0; /// All data is buffered, let go of mysql c api structures CleanUp(); }
VALUE db_mysql_result_from_statement(VALUE self, VALUE statement) { int n, row, cols; MYSQL_STMT *s; MYSQL_FIELD *fields; MYSQL_RES *result; Result *r = db_mysql_result_handle(self); if (!rb_obj_is_kind_of(statement, cDMS)) rb_raise(eSwiftArgumentError, "invalid Mysql::Statement"); r->statement = statement; s = db_mysql_statement_handle_safe(statement)->statement; mysql_stmt_store_result(s); result = mysql_stmt_result_metadata(s); db_mysql_result_load(self, result, mysql_stmt_insert_id(s), mysql_stmt_affected_rows(s)); if (result) { cols = mysql_num_fields(result); fields = mysql_fetch_fields(result); r->bind = (MYSQL_BIND *)malloc(sizeof(MYSQL_BIND) * cols); r->lengths = (unsigned long *)malloc(sizeof(unsigned long) * cols); r->is_null = (my_bool *)malloc(sizeof(my_bool) * cols); memset(r->bind, 0, sizeof(MYSQL_BIND) * cols); for (n = 0; n < cols; n++) { r->bind[n].length = &r->lengths[n]; r->bind[n].is_null = &r->is_null[n]; r->bind[n].buffer_type = fields[n].type; switch(fields[n].type) { case MYSQL_TYPE_NULL: r->bind[n].buffer = malloc(1); r->bind[n].buffer_length = 1; break; case MYSQL_TYPE_TINY: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_YEAR: case MYSQL_TYPE_INT24: case MYSQL_TYPE_LONG: case MYSQL_TYPE_LONGLONG: case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: r->bind[n].buffer = malloc(8); r->bind[n].buffer_length = 8; memset(r->bind[n].buffer, 0, 8); break; case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_NEWDECIMAL: case MYSQL_TYPE_BIT: r->bind[n].buffer = malloc(fields[n].length); r->bind[n].buffer_length = fields[n].length; memset(r->bind[n].buffer, 0, fields[n].length); if (!(fields[n].flags & BINARY_FLAG)) r->bind[n].buffer_type = MYSQL_TYPE_STRING; break; case MYSQL_TYPE_TIME: case MYSQL_TYPE_DATE: case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_TIMESTAMP: r->bind[n].buffer = malloc(sizeof(MYSQL_TIME)); r->bind[n].buffer_length = sizeof(MYSQL_TIME); memset(r->bind[n].buffer, 0, sizeof(MYSQL_TIME)); break; default: rb_raise(rb_eTypeError, "unknown buffer_type: %d", fields[n].type); } } if (mysql_stmt_bind_result(s, r->bind) != 0) rb_raise(eSwiftRuntimeError, "%s", mysql_stmt_error(s)); } r->start = mysql_stmt_row_tell(s); r->selected = mysql_stmt_num_rows(s); r->affected = mysql_stmt_affected_rows(s); return self; }
unsigned int wxMySQLRecordset::GetRowCount() { return mysql_stmt_num_rows(m_Stmt); }
PreparedResultSet::PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES *result, uint64 rowCount, uint32 fieldCount) : m_rowCount(rowCount), m_rowPosition(0), m_fieldCount(fieldCount), m_rBind(nullptr), m_stmt(stmt), m_metadataResult(result) { if (!m_metadataResult) return; if (m_stmt->bind_result_done) { delete[] m_stmt->bind->length; delete[] m_stmt->bind->is_null; } m_rBind = new MYSQL_BIND[m_fieldCount]; //- for future readers wondering where the f**k this is freed - mysql_stmt_bind_result moves pointers to these // from m_rBind to m_stmt->bind and it is later freed by the `if (m_stmt->bind_result_done)` block just above here // MYSQL_STMT lifetime is equal to connection lifetime my_bool* m_isNull = new my_bool[m_fieldCount]; unsigned long* m_length = new unsigned long[m_fieldCount]; memset(m_isNull, 0, sizeof(my_bool) * m_fieldCount); memset(m_rBind, 0, sizeof(MYSQL_BIND) * m_fieldCount); memset(m_length, 0, sizeof(unsigned long) * m_fieldCount); //- This is where we store the (entire) resultset if (mysql_stmt_store_result(m_stmt)) { TC_LOG_WARN("sql.sql", "%s:mysql_stmt_store_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt)); delete[] m_rBind; delete[] m_isNull; delete[] m_length; return; } m_rowCount = mysql_stmt_num_rows(m_stmt); //- This is where we prepare the buffer based on metadata MYSQL_FIELD* field = mysql_fetch_fields(m_metadataResult); std::size_t rowSize = 0; for (uint32 i = 0; i < m_fieldCount; ++i) { uint32 size = SizeForType(&field[i]); rowSize += size; m_rBind[i].buffer_type = field[i].type; m_rBind[i].buffer_length = size; m_rBind[i].length = &m_length[i]; m_rBind[i].is_null = &m_isNull[i]; m_rBind[i].error = nullptr; m_rBind[i].is_unsigned = field[i].flags & UNSIGNED_FLAG; } char* dataBuffer = new char[rowSize * m_rowCount]; for (uint32 i = 0, offset = 0; i < m_fieldCount; ++i) { m_rBind[i].buffer = dataBuffer + offset; offset += m_rBind[i].buffer_length; } //- This is where we bind the bind the buffer to the statement if (mysql_stmt_bind_result(m_stmt, m_rBind)) { TC_LOG_WARN("sql.sql", "%s:mysql_stmt_bind_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt)); mysql_stmt_free_result(m_stmt); CleanUp(); delete[] m_isNull; delete[] m_length; return; } m_rows.resize(uint32(m_rowCount) * m_fieldCount); while (_NextRow()) { for (uint32 fIndex = 0; fIndex < m_fieldCount; ++fIndex) { unsigned long buffer_length = m_rBind[fIndex].buffer_length; unsigned long fetched_length = *m_rBind[fIndex].length; if (!*m_rBind[fIndex].is_null) { void* buffer = m_stmt->bind[fIndex].buffer; switch (m_rBind[fIndex].buffer_type) { case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: // warning - the string will not be null-terminated if there is no space for it in the buffer // when mysql_stmt_fetch returned MYSQL_DATA_TRUNCATED // we cannot blindly null-terminate the data either as it may be retrieved as binary blob and not specifically a string // in this case using Field::GetCString will result in garbage // TODO: remove Field::GetCString and use boost::string_ref (currently proposed for TS as string_view, maybe in C++17) if (fetched_length < buffer_length) *((char*)buffer + fetched_length) = '\0'; break; default: break; } m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue( buffer, MysqlTypeToFieldType(m_rBind[fIndex].buffer_type), fetched_length); // move buffer pointer to next part m_stmt->bind[fIndex].buffer = (char*)buffer + rowSize; } else { m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue( nullptr, MysqlTypeToFieldType(m_rBind[fIndex].buffer_type), *m_rBind[fIndex].length); } #ifdef TRINITY_STRICT_DATABASE_TYPE_CHECKS m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetMetadata(&field[fIndex], fIndex); #endif } m_rowPosition++; } m_rowPosition = 0; /// All data is buffered, let go of mysql c api structures mysql_stmt_free_result(m_stmt); }
bool MySQLConnection::_Query(PreparedStatement* stmt, MYSQL_RES **pResult, uint64* pRowCount, uint32* pFieldCount) { if (!m_Mysql) return false; uint32 index = stmt->m_index; { MySQLPreparedStatement* m_mStmt = GetPreparedStatement(index); ASSERT(m_mStmt); // Can only be null if preparation failed, server side error or bad query m_mStmt->m_stmt = stmt; // Cross reference them for debug output stmt->m_stmt = m_mStmt; // TODO: Cleaner way stmt->BindParameters(); MYSQL_STMT* msql_STMT = m_mStmt->GetSTMT(); MYSQL_BIND* msql_BIND = m_mStmt->GetBind(); uint32 _s = 0; if (sLog->GetSQLDriverQueryLogging()) _s = getMSTime(); if (mysql_stmt_bind_param(msql_STMT, msql_BIND)) { uint32 lErrno = mysql_errno(m_Mysql); sLog->outSQLDriver("SQL(p): %s\n [ERROR]: [%u] %s", m_mStmt->getQueryString(m_queries[index].first).c_str(), lErrno, mysql_stmt_error(msql_STMT)); if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection) return _Query(stmt, pResult, pRowCount, pFieldCount); // Try again m_mStmt->ClearParameters(); return false; } if (mysql_stmt_execute(msql_STMT)) { uint32 lErrno = mysql_errno(m_Mysql); sLog->outSQLDriver("SQL(p): %s\n [ERROR]: [%u] %s", m_mStmt->getQueryString(m_queries[index].first).c_str(), lErrno, mysql_stmt_error(msql_STMT)); if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection) return _Query(stmt, pResult, pRowCount, pFieldCount); // Try again m_mStmt->ClearParameters(); return false; } if (sLog->GetSQLDriverQueryLogging()) sLog->outSQLDriver("[%u ms] SQL(p): %s", getMSTimeDiff(_s, getMSTime()), m_mStmt->getQueryString(m_queries[index].first).c_str()); m_mStmt->ClearParameters(); *pResult = mysql_stmt_result_metadata(msql_STMT); *pRowCount = mysql_stmt_num_rows(msql_STMT); *pFieldCount = mysql_stmt_field_count(msql_STMT); return true; } }
int mysql_drv_store_results(db_result_set_t *rs) { MYSQL *con = rs->connection->ptr; MYSQL_RES *res; MYSQL_ROW row; int rc; #ifdef HAVE_PS /* Is this result set from prepared statement? */ if (rs->statement != NULL && rs->statement->emulated == 0) { if (rs->statement->ptr == NULL) return 1; rc = mysql_stmt_store_result(rs->statement->ptr); DEBUG("mysql_stmt_store_result(%p) = %d", rs->statement->ptr, rc); if (rc) { rc = mysql_errno(con); DEBUG("mysql_errno(%p) = %d", con, rc); if (rc == ER_LOCK_DEADLOCK || rc == ER_LOCK_WAIT_TIMEOUT) { log_text(LOG_WARNING, "mysql_stmt_store_result() failed with error: (%d) %s", rc, mysql_error(con)); return SB_DB_ERROR_DEADLOCK; } log_text(LOG_ALERT, "MySQL error: %s\n", mysql_error(con)); return SB_DB_ERROR_FAILED; } rs->nrows = mysql_stmt_num_rows(rs->statement->ptr); DEBUG("mysql_stmt_num_rows(%p) = %d", rs->statement->ptr, rs->nrows); do { rc = mysql_stmt_fetch(rs->statement->ptr); DEBUG("mysql_stmt_fetch(%p) = %d", rs->statement->ptr, rc); } while(rc == 0); return SB_DB_ERROR_NONE; } #endif if (con == NULL) return SB_DB_ERROR_FAILED; /* using store results for speed will not work for large sets */ res = mysql_store_result(con); DEBUG("mysql_store_result(%p) = %p", con, res); if (res == NULL) { rc = mysql_errno(con); DEBUG("mysql_errno(%p) = %u", con, rc); if (rc == ER_LOCK_DEADLOCK || rc == ER_LOCK_WAIT_TIMEOUT) { log_text(LOG_WARNING, "mysql_store_result() failed with error: (%u) %s", rc, mysql_error(con)); return SB_DB_ERROR_DEADLOCK; } log_text(LOG_ALERT, "MySQL error: %s", mysql_error(con)); return SB_DB_ERROR_FAILED; } rs->ptr = (void *)res; rs->nrows = mysql_num_rows(res); DEBUG("mysql_num_rows(%p) = %u", res, rs->nrows); /* just fetch result */ while((row = mysql_fetch_row(res))) DEBUG("mysql_fetch_row(%p) = %p", res, row); return SB_DB_ERROR_NONE; }