-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathOutputBinder.cpp
140 lines (122 loc) · 4.42 KB
/
OutputBinder.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include "MySqlException.hpp"
#include "MySqlPreparedStatement.hpp"
#include "OutputBinder.hpp"
#include <cassert>
#include <mysql/mysql.h>
#include <boost/lexical_cast.hpp>
#include <string>
#include <tuple>
#include <vector>
using boost::lexical_cast;
using std::get;
using std::string;
using std::tuple;
using std::vector;
namespace OutputBinderPrivate {
void Friend::throwIfParameterCountWrong(
const size_t numRequiredParameters,
const MySqlPreparedStatement& statement
) {
// Check that the sizes match
if (statement.getFieldCount() != numRequiredParameters) {
string errorMessage(
"Incorrect number of output parameters; query required ");
errorMessage += lexical_cast<string>(statement.getFieldCount());
errorMessage += " but ";
errorMessage += lexical_cast<string>(numRequiredParameters);
errorMessage += " parameters were provided";
throw MySqlException(errorMessage);
}
}
int Friend::bindAndExecuteStatement(
vector<MYSQL_BIND>* const parameters,
const MySqlPreparedStatement& statement
) {
if (0 != mysql_stmt_bind_result(
statement.statementHandle_,
parameters->data()))
{
throw MySqlException(mysql_stmt_error(statement.statementHandle_));
}
if (0 != mysql_stmt_execute(statement.statementHandle_)) {
throw MySqlException(mysql_stmt_error(statement.statementHandle_));
}
return mysql_stmt_fetch(statement.statementHandle_);
}
void Friend::throwIfFetchError(
const int fetchStatus,
const MySqlPreparedStatement& statement
) {
switch (fetchStatus) {
case MYSQL_NO_DATA:
// No problem! All rows fetched.
break;
case 1: { // Error occurred {
throw MySqlException(mysql_stmt_error(statement.statementHandle_));
}
default: {
assert(false && "Unknown error code from mysql_stmt_fetch");
throw MySqlException(mysql_stmt_error(statement.statementHandle_));
}
}
}
void Friend::refetchTruncatedColumns(
const MySqlPreparedStatement& statement,
vector<MYSQL_BIND>* const parameters,
vector<vector<char>>* const buffers,
vector<mysql_bind_length_t>* const lengths
) {
// Find which buffers were too small, expand them and refetch
typedef unsigned int mysql_column_t;
typedef unsigned long mysql_offset_t;
vector<tuple<mysql_column_t, mysql_offset_t>> truncatedColumns;
for (size_t i = 0; i < lengths->size(); ++i) {
vector<char>& buffer = buffers->at(i);
const size_t untruncatedLength = lengths->at(i);
if (untruncatedLength > buffer.size()) {
// Only refetch the part that we didn't get the first time
const size_t alreadyRetrieved = buffer.size();
truncatedColumns.push_back(
tuple<size_t, size_t>(i, alreadyRetrieved));
buffer.resize(untruncatedLength + 1);
MYSQL_BIND& bind = parameters->at(i);
bind.buffer = &buffer.at(alreadyRetrieved);
bind.buffer_length = buffer.size() - alreadyRetrieved - 1;
}
}
// I'm not sure why, but I occasionally get the truncated status
// code when nothing was truncated... so just break out?
if (truncatedColumns.empty()) {
// No truncations!
return;
}
// Refetch only the data that were truncated
for (const auto& i : truncatedColumns) {
const mysql_column_t column = get<0>(i);
const mysql_offset_t offset = get<1>(i);
MYSQL_BIND& parameter = parameters->at(column);
const int status = mysql_stmt_fetch_column(
statement.statementHandle_,
¶meter,
column,
offset);
if (0 != status) {
throw MySqlException(mysql_stmt_error(statement.statementHandle_));
}
// Now, for subsequent fetches, we need to reset the buffers
vector<char>& buffer = buffers->at(column);
parameter.buffer = buffer.data();
parameter.buffer_length = buffer.size();
}
// If we've changed the buffers, we need to rebind
if (0 != mysql_stmt_bind_result(
statement.statementHandle_,
parameters->data()))
{
throw MySqlException(mysql_stmt_error(statement.statementHandle_));
}
}
int Friend::fetch(const MySqlPreparedStatement& statement) {
return mysql_stmt_fetch(statement.statementHandle_);
}
} // namespace OutputBinderPrivate