The Battle for Wesnoth  1.15.3+dev
result_set.cpp
Go to the documentation of this file.
1 //
2 // M A R I A D B + +
3 //
4 // Copyright Sylvain Rochette Langlois 2013,
5 // Frantisek Boranek 2015,
6 // The ViaDuck Project 2016 - 2018.
7 // Distributed under the Boost Software License, Version 1.0.
8 // (See accompanying file LICENSE or copy at
9 // http://www.boost.org/LICENSE_1_0.txt)
10 
11 #include <mysql.h>
12 #include <memory.h>
13 #include <mariadb++/connection.hpp>
14 #include <mariadb++/result_set.hpp>
16 #include <mariadb++/bind.hpp>
17 #include "private.hpp"
18 
19 using namespace mariadb;
20 
22  : m_result_set((conn->account()->store_result() ? mysql_store_result : mysql_use_result)(conn->m_mysql)),
23  m_fields(nullptr),
24  m_row(nullptr),
25  m_raw_binds(nullptr),
26  m_stmt_data(nullptr),
27  m_lengths(nullptr),
28  m_field_count(0),
29  m_was_fetched(false) {
30 
31  if (m_result_set) {
32  m_field_count = mysql_num_fields(m_result_set);
33  m_fields = mysql_fetch_fields(m_result_set);
34 
35  for (u32 i = 0; i < m_field_count; ++i) m_indexes[m_fields[i].name] = i;
36  }
37 }
38 
40  : m_result_set(nullptr),
41  m_fields(nullptr),
42  m_row(nullptr),
43  m_raw_binds(nullptr),
44  m_stmt_data(stmt_data),
45  m_lengths(nullptr),
46  m_field_count(0),
47  m_was_fetched(false) {
48 
49  int max_length = 1;
50  mysql_stmt_attr_set(stmt_data->m_statement, STMT_ATTR_UPDATE_MAX_LENGTH, &max_length);
51 
52  if (conn->account()->store_result() && mysql_stmt_store_result(stmt_data->m_statement))
53  STMT_ERROR(stmt_data->m_statement)
54  else {
55  m_field_count = mysql_stmt_field_count(stmt_data->m_statement);
56  m_result_set = mysql_stmt_result_metadata(stmt_data->m_statement);
57 
58  if (m_field_count > 0) {
59  m_fields = mysql_fetch_fields(m_result_set);
60  m_raw_binds = new MYSQL_BIND[m_field_count];
61  m_row = new char *[m_field_count];
62 
63  for (u32 i = 0; i < m_field_count; ++i) {
64  m_indexes[m_fields[i].name] = i;
65  m_binds.emplace_back(new bind(&m_raw_binds[i], &m_fields[i]));
66  m_row[i] = m_binds[i]->buffer();
67  }
68 
69  mysql_stmt_bind_result(stmt_data->m_statement, m_raw_binds);
70  }
71  }
72 }
73 
75  delete[] m_raw_binds;
76 
77  if (m_statement) mysql_stmt_close(m_statement);
78 }
79 
81  if (m_result_set) mysql_free_result(m_result_set);
82 
83  delete[] m_raw_binds;
84 
85  if (m_stmt_data) {
86  delete[] m_row;
87 
88  mysql_stmt_free_result(m_stmt_data->m_statement);
89  }
90 }
91 
93 
95  if (index >= m_field_count) throw std::out_of_range("Column index out of range");
96 
97  bool is_unsigned = (m_fields[index].flags & UNSIGNED_FLAG) == UNSIGNED_FLAG;
98 
99  switch (m_fields[index].type) {
100  case MYSQL_TYPE_NULL:
101  return value::null;
102 
103  case MYSQL_TYPE_BIT:
104  return value::boolean;
105 
106  case MYSQL_TYPE_FLOAT:
107  return value::float32;
108 
109  case MYSQL_TYPE_DECIMAL:
110  case MYSQL_TYPE_NEWDECIMAL:
111  return value::decimal;
112 
113  case MYSQL_TYPE_DOUBLE:
114  return value::double64;
115 
116  case MYSQL_TYPE_NEWDATE:
117  case MYSQL_TYPE_DATE:
118  return value::date;
119 
120  case MYSQL_TYPE_TIME:
121  return value::time;
122 
123  case MYSQL_TYPE_TIMESTAMP:
124  case MYSQL_TYPE_DATETIME:
125  return value::date_time;
126 
127  case MYSQL_TYPE_TINY:
128  return (is_unsigned ? value::unsigned8 : value::signed8);
129 
130  case MYSQL_TYPE_YEAR:
131  case MYSQL_TYPE_SHORT:
132  return (is_unsigned ? value::unsigned16 : value::signed16);
133 
134  case MYSQL_TYPE_INT24:
135  case MYSQL_TYPE_LONG:
136  return (is_unsigned ? value::unsigned32 : value::signed32);
137 
138  case MYSQL_TYPE_LONGLONG:
139  return (is_unsigned ? value::unsigned64 : value::signed64);
140 
141  case MYSQL_TYPE_TINY_BLOB:
142  case MYSQL_TYPE_MEDIUM_BLOB:
143  case MYSQL_TYPE_LONG_BLOB:
144  case MYSQL_TYPE_BLOB:
145  return value::blob;
146 
147  case MYSQL_TYPE_ENUM:
148  return value::enumeration;
149 
150  default:
151  case MYSQL_TYPE_VARCHAR:
152  case MYSQL_TYPE_VAR_STRING:
153  case MYSQL_TYPE_STRING:
154  return value::string;
155  }
156 }
157 
159  if (index >= m_field_count) throw std::out_of_range("Column index out of range");
160 
161  return m_fields[index].name;
162 }
163 
165  const map_indexes_t::const_iterator i = m_indexes.find(name);
166 
167  if (i == m_indexes.end()) return 0xffffffff;
168 
169  return i->second;
170 }
171 
172 unsigned long result_set::column_size(u32 index) const {
173  if (index >= m_field_count) throw std::out_of_range("Column index out of range");
174 
175  return m_stmt_data ? m_binds.at(index)->length() : m_lengths[index];
176 }
177 
179  if (m_stmt_data)
180  mysql_stmt_data_seek(m_stmt_data->m_statement, index);
181  else
182  mysql_data_seek(m_result_set, index);
183  return next();
184 }
185 
187  if (!m_result_set) return (m_was_fetched = false);
188 
189  if (m_stmt_data) return (m_was_fetched = !mysql_stmt_fetch(m_stmt_data->m_statement));
190 
191  m_row = mysql_fetch_row(m_result_set);
192  m_lengths = mysql_fetch_lengths(m_result_set);
193 
194  // make sure no access to results is possible until a result is successfully fetched
195  return (m_was_fetched = m_row != nullptr);
196 }
197 
199  if (m_stmt_data) return reinterpret_cast<u64>(mysql_stmt_row_tell(m_stmt_data->m_statement));
200 
201  return reinterpret_cast<u64>(mysql_row_tell(m_result_set));
202 }
203 
205  if (m_stmt_data) return mysql_stmt_num_rows(m_stmt_data->m_statement);
206 
207  return mysql_num_rows(m_result_set);
208 }
209 
211  if (!m_was_fetched) throw std::out_of_range("No row was fetched");
212 }
213 
214 void result_set::check_type(u32 index, value::type requested) const {
215  value::type actual = column_type(index);
216  bool type_error;
217 
218  // check requested type vs actual type.
219  switch (requested) {
220  // these types require exact type match
224 
225  case value::type::time:
227  case value::type::date:
228 
230  type_error = actual != requested;
231  break;
232 
233  // these types require size match
236  type_error = actual != value::type::signed8 && actual != value::type::unsigned8;
237  break;
240  type_error = actual != value::type::signed16 && actual != value::type::unsigned16;
241  break;
244  type_error = actual != value::type::signed32 && actual != value::type::unsigned32;
245  break;
248  type_error = actual != value::type::signed64 && actual != value::type::unsigned64;
249  break;
250 
251  // bool can also be a signed8
253  type_error = actual != requested && actual != value::type::signed8;
254  break;
255 
256  // only string, blob, data and null are interchangeable
257  case value::type::string:
258  case value::type::blob:
259  case value::type::data:
260  type_error = actual != value::type::string &&
261  actual != value::type::blob &&
262  actual != value::type::data &&
263  actual != value::type::null;
264  break;
265 
266  default:
267  type_error = false;
268  }
269 
270  if (type_error) {
271  printf("requested type %d does not match actual type %d", +requested, +actual);
272  MARIADB_ERROR_THROW_CONNECTION(12, "type error");
273  }
274 }
275 
277  size_t len = column_size(index);
278 
279  if (len == 0) return stream_ref();
280 
281  auto *ss = new std::istringstream();
282  ss->rdbuf()->pubsetbuf(m_row[index], len);
283  return stream_ref(ss);
284 }
285 
286 MAKE_GETTER(data, data_ref, value::type::data)
287  size_t len = column_size(index);
288 
289  return len == 0 ? data_ref() : data_ref(new data<char>(m_row[index], len));
290 }
291 
292 MAKE_GETTER(string, std::string, value::type::string)
293  return std::string(m_row[index], column_size(index));
294 }
295 
298 
299  return date_time(std::string(m_row[index], column_size(index))).date();
300 }
301 
304 
305  return date_time(std::string(m_row[index], column_size(index)));
306 }
307 
308 MAKE_GETTER(time, mariadb::time, value::type::time)
310 
311  return mariadb::time(std::string(m_row[index], column_size(index)));
312 }
313 
315  return decimal(std::string(m_row[index], column_size(index)));
316 }
317 
318 MAKE_GETTER(boolean, bool, value::type::boolean)
319  if (m_stmt_data) return (m_binds[index]->m_uchar8[0] != 0);
320 
321  return string_cast<bool>(std::string(m_row[index], column_size(index)));
322 }
323 
325  if (m_stmt_data) return checked_cast<u8>(0x00000000000000ff & m_binds[index]->m_unsigned64);
326 
327  return string_cast<u8>(std::string(m_row[index], column_size(index)));
328 }
329 
330 MAKE_GETTER(signed8, s8, value::type::signed8)
331  if (m_stmt_data) return checked_cast<s8>(0x00000000000000ff & m_binds[index]->m_signed64);
332 
333  return string_cast<s8>(std::string(m_row[index], column_size(index)));
334 }
335 
337  if (m_stmt_data) return checked_cast<u16>(0x000000000000ffff & m_binds[index]->m_unsigned64);
338 
339  return string_cast<u16>(std::string(m_row[index], column_size(index)));
340 }
341 
343  if (m_stmt_data) return checked_cast<s16>(0x000000000000ffff & m_binds[index]->m_signed64);
344 
345  return string_cast<s16>(std::string(m_row[index], column_size(index)));
346 }
347 
349  if (m_stmt_data) return checked_cast<u32>(0x00000000ffffffff & m_binds[index]->m_unsigned64);
350 
351  return string_cast<u32>(std::string(m_row[index], column_size(index)));
352 }
353 
355  if (m_stmt_data) return m_binds[index]->m_signed32[0];
356 
357  return string_cast<s32>(std::string(m_row[index], column_size(index)));
358 }
359 
362 
363  return string_cast<u64>(std::string(m_row[index], column_size(index)));
364 }
365 
368 
369  return string_cast<s64>(std::string(m_row[index], column_size(index)));
370 }
371 
372 MAKE_GETTER(float, f32, value::type::float32)
373  if (m_stmt_data) return m_binds[index]->m_float32[0];
374 
375  return string_cast<f32>(std::string(m_row[index], column_size(index)));
376 }
377 
378 MAKE_GETTER(double, f64, value::type::double64)
380 
381  return string_cast<f64>(std::string(m_row[index], column_size(index)));
382 }
383 
384 MAKE_GETTER(is_null, bool, value::type::null)
385  if (m_stmt_data) return m_binds[index]->is_null();
386 
387  return !m_row[index];
388 }
signed int s32
Definition: types.hpp:25
MYSQL_FIELD * m_fields
Definition: result_set.hpp:198
u32 column_count() const
Get the count of columns contained in this result_set.
Definition: result_set.cpp:92
Class representing SQL time.
Definition: time.hpp:23
#define MARIADB_ERROR_THROW_CONNECTION(error_id, error)
Definition: private.hpp:44
double f64
Definition: types.hpp:27
void check_row_fetched() const
Throws if the result set was created, but no row was ever fetched (using next())
Definition: result_set.cpp:210
value::type column_type(u32 index) const
Gets the type of a column by index.
Definition: result_set.cpp:94
return decimal(std::string(m_row[index], column_size(index)))
STL namespace.
const std::string column_name(u32 index)
Gets the name of a column by index.
Definition: result_set.cpp:158
float f32
Definition: types.hpp:26
Class used to store account and connection information used by mariadb::connection when connecting...
Definition: account.hpp:46
T string_cast(const std::string &str)
T checked_cast(K value)
std::shared_ptr< std::istream > stream_ref
Definition: types.hpp:78
size_t len
Definition: result_set.cpp:277
std::vector< bind_ref > m_binds
Definition: result_set.hpp:205
if(len==0) return stream_ref()
bool next()
Fetch next row from result_set.
Definition: result_set.cpp:186
unsigned long column_size(u32 index) const
Gets the size of the data contained in the column at index.
Definition: result_set.cpp:172
map_indexes_t m_indexes
Definition: result_set.hpp:209
std::shared_ptr< ::mariadb::data< char > > data_ref
Definition: data.hpp:170
bind m_unsigned64
Definition: statement.cpp:119
account_ref account() const
Gets the account associated with this connection.
Definition: connection.cpp:63
unsigned char u8
Definition: types.hpp:20
result_set(connection *conn)
Create result_set from connection.
Definition: result_set.cpp:21
u32 column_index(const std::string &name) const
Get the index of a column by column-name (case sensitive)
Definition: result_set.cpp:164
if(m_stmt_data) return mariadb return date_time(std::string(m_row[index], column_size(index))).date()
unsigned short u16
Definition: types.hpp:21
bind m_signed64
Definition: statement.cpp:129
signed long long s64
Definition: types.hpp:35
void check_type(u32 index, value::type requested) const
Throws if the actual type of column at index cannot be converted to the requested type...
Definition: result_set.cpp:214
std::size_t i
Definition: function.cpp:933
virtual ~result_set()
Destructs the result_set and frees all result data.
Definition: result_set.cpp:80
Class used to represent SQL date_time.
Definition: date_time.hpp:20
Wraps a Database connection.
Definition: connection.hpp:27
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
u64 row_count() const
Gets the number of rows in this result.
Definition: result_set.cpp:204
MYSQL_RES * m_result_set
Definition: result_set.hpp:196
ss rdbuf() -> pubsetbuf(m_row[index], len)
long unsigned int * m_lengths
Definition: result_set.hpp:211
std::shared_ptr< statement_data > statement_data_ref
Definition: result_set.hpp:64
signed short s16
Definition: types.hpp:24
bind m_time
Definition: statement.cpp:95
unsigned long long u64
Definition: types.hpp:34
#define STMT_ERROR(statement)
Definition: private.hpp:70
MYSQL_BIND * m_raw_binds
Definition: result_set.hpp:202
statement_data_ref m_stmt_data
Definition: result_set.hpp:207
u64 row_index() const
Gets the row index of the currently selected row.
Definition: result_set.cpp:198
bool set_row_index(u64 index)
Set the current row index in result_set (seek to result).
Definition: result_set.cpp:178
#define MAKE_GETTER(nm, rtype, vtype)
Definition: result_set.hpp:29
bind m_float32[0]
Definition: statement.cpp:164
unsigned int u32
Definition: types.hpp:22
signed char s8
Definition: types.hpp:23
bind m_double64
Definition: statement.cpp:169
auto * ss
Definition: result_set.cpp:281