forked from bskari/mysql-cpp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMySql.hpp
229 lines (194 loc) · 7.22 KB
/
MySql.hpp
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#ifndef MYSQL_HPP_
#define MYSQL_HPP_
#include <cassert>
#include <cstdint>
#include <cstring>
#include <mysql/mysql.h>
#include <boost/lexical_cast.hpp>
#include <string>
#include <tuple>
#include <typeinfo>
#include <utility>
#include <vector>
#include "InputBinder.hpp"
#include "MySqlException.hpp"
#include "MySqlPreparedStatement.hpp"
#include "OutputBinder.hpp"
#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)
#ifndef nullptr
// I know that this isn't a perfect substitution, but I'm lazy
#define nullptr 0
#endif
#endif
class MySql {
public:
MySql(
const char* const hostname,
const char* const username,
const char* const password,
const char* const database,
const uint16_t port = 3306);
MySql(
const char* hostname,
const char* username,
const char* password,
const uint16_t port = 3306);
~MySql();
MySql(const MySql& rhs) = delete;
MySql(MySql&& rhs) = delete;
MySql& operator=(const MySql& rhs) = delete;
MySql& operator=(MySql&& rhs) = delete;
/**
* Normal query. Results are stored in the given vector.
* @param query The query to run.
* @param results A vector of tuples to store the results in.
* @param args Arguments to bind to the query.
*/
template <typename... InputArgs, typename... OutputArgs>
void runQuery(
std::vector<std::tuple<OutputArgs...>>* const results,
const char* const query,
// Args needs to be sent by reference, because the values need to be
// nontemporary (that is, lvalues) so that their memory locations
// can be bound to MySQL's prepared statement API
const InputArgs&... args) const;
/**
* Command that doesn't return results, like "USE yelp" or
* "INSERT INTO user VALUES ('Brandon', 28)".
* @param query The query to run.
* @param args Arguments to bind to the query.
* @return The number of affected rows.
*/
/// @{
template <typename... Args>
my_ulonglong runCommand(
const char* const command,
// Args needs to be sent by reference, because the values need to be
// nontemporary (that is, lvalues) so that their memory locations
// can be bound to MySQL's prepared statement API
const Args&... args);
my_ulonglong runCommand(const char* const command);
/// @}
/**
* Prepare a statement for multiple executions with different bound
* parameters. If you're running a one off query or statement, you
* should use runQuery or runCommand instead.
* @param query The query to prepare.
* @return A prepared statement object.
*/
MySqlPreparedStatement prepareStatement(const char* statement) const;
/**
* Run the command version of a prepared statement.
*/
/// @{
template <typename... Args>
my_ulonglong runCommand(
const MySqlPreparedStatement& statement,
const Args&... args);
my_ulonglong runCommand(const MySqlPreparedStatement& statement);
/// @}
/**
* Run the query version of a prepared statement.
*/
template <typename... InputArgs, typename... OutputArgs>
void runQuery(
std::vector<std::tuple<OutputArgs...>>* results,
const MySqlPreparedStatement& statement,
const InputArgs&...) const;
private:
MYSQL* connection_;
};
template <typename... Args>
my_ulonglong MySql::runCommand(
const char* const command,
const Args&... args
) {
MySqlPreparedStatement statement(prepareStatement(command));
return runCommand(statement, args...);
}
template <typename... Args>
my_ulonglong MySql::runCommand(
const MySqlPreparedStatement& statement,
const Args&... args
) {
// Commands (e.g. INSERTs or DELETEs) should always have this set to 0
if (0 != statement.getFieldCount()) {
throw MySqlException("Tried to run query with runCommand");
}
if (sizeof...(args) != statement.getParameterCount()) {
std::string errorMessage;
errorMessage += "Incorrect number of parameters; command required ";
errorMessage += boost::lexical_cast<std::string>(
statement.getParameterCount());
errorMessage += " but ";
errorMessage += boost::lexical_cast<std::string>(sizeof...(args));
errorMessage += " parameters were provided.";
throw MySqlException(errorMessage);
}
std::vector<MYSQL_BIND> bindParameters;
bindParameters.resize(statement.getParameterCount());
bindInputs<Args...>(&bindParameters, args...);
if (0 != mysql_stmt_bind_param(
statement.statementHandle_,
bindParameters.data())
) {
throw MySqlException(statement);
}
if (0 != mysql_stmt_execute(statement.statementHandle_)) {
throw MySqlException(statement);
}
// If the user ran a SELECT statement or something else, at least warn them
const auto affectedRows = mysql_stmt_affected_rows(
statement.statementHandle_);
if ((static_cast<decltype(affectedRows)>(-1)) == affectedRows) {
throw MySqlException("Tried to run query with runCommand");
}
return affectedRows;
}
template <typename... InputArgs, typename... OutputArgs>
void MySql::runQuery(
std::vector<std::tuple<OutputArgs...>>* const results,
const char* const query,
const InputArgs&... args
) const {
assert(nullptr != results);
assert(nullptr != query);
MySqlPreparedStatement statement(prepareStatement(query));
runQuery(results, statement, args...);
}
template <typename... InputArgs, typename... OutputArgs>
void MySql::runQuery(
std::vector<std::tuple<OutputArgs...>>* const results,
const MySqlPreparedStatement& statement,
const InputArgs&... args
) const {
assert(nullptr != results);
// SELECTs should always return something. Commands (e.g. INSERTs or
// DELETEs) should always have this set to 0.
if (0 == statement.getFieldCount()) {
throw MySqlException("Tried to run command with runQuery");
}
// Bind the input parameters
// Check that the parameter count is right
if (sizeof...(InputArgs) != statement.getParameterCount()) {
std::string errorMessage;
errorMessage += "Incorrect number of input parameters; query required ";
errorMessage += boost::lexical_cast<std::string>(
statement.getParameterCount());
errorMessage += " but ";
errorMessage += boost::lexical_cast<std::string>(sizeof...(args));
errorMessage += " parameters were provided.";
throw MySqlException(errorMessage);
}
std::vector<MYSQL_BIND> inputBindParameters;
inputBindParameters.resize(statement.getParameterCount());
bindInputs<InputArgs...>(&inputBindParameters, args...);
if (0 != mysql_stmt_bind_param(
statement.statementHandle_,
inputBindParameters.data())
) {
throw MySqlException(statement);
}
setResults<OutputArgs...>(statement, results);
}
#endif // MYSQL_HPP_