Kea 2.0.0
mysql_binding.cc
Go to the documentation of this file.
1// Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
11#include <boost/date_time/gregorian/gregorian.hpp>
12#include <mysql/mysql_binding.h>
13
14using namespace boost::posix_time;
15using namespace isc::asiolink;
16using namespace isc::data;
17using namespace isc::util;
18
19namespace isc {
20namespace db {
21
22std::string
24 // Make sure the binding type is text.
25 validateAccess<std::string>();
26 if (length_ == 0) {
27 return (std::string());
28 }
29 return (std::string(buffer_.begin(), buffer_.begin() + length_));
30}
31
32std::string
33MySqlBinding::getStringOrDefault(const std::string& default_value) const {
34 if (amNull()) {
35 return (default_value);
36 }
37 return (getString());
38}
39
42 if (amNull()) {
43 return (ElementPtr());
44 }
45 std::string s = getString();
46 return (Element::fromJSON(s));
47}
48
49std::vector<uint8_t>
51 // Make sure the binding type is blob.
52 validateAccess<std::vector<uint8_t> >();
53 if (length_ == 0) {
54 return (std::vector<uint8_t>());
55 }
56 return (std::vector<uint8_t>(buffer_.begin(), buffer_.begin() + length_));
57}
58
59std::vector<uint8_t>
60MySqlBinding::getBlobOrDefault(const std::vector<uint8_t>& default_value) const {
61 if (amNull()) {
62 return (default_value);
63 }
64 return (getBlob());
65}
66
67float
69 // It may seem a bit weird that we use getInteger template method
70 // for getting a floating point value. However, the getInteger method
71 // seems to be generic enough to support it. If we were to redo the
72 // API of this class we would probably introduce a getNumericValue
73 // method instead of getInteger. However, we already have getInteger
74 // used in many places so we should stick to it.
75 return (getInteger<float>());
76}
77
78ptime
80 // Make sure the binding type is timestamp.
81 validateAccess<ptime>();
82 // Copy the buffer contents into native timestamp structure and
83 // then convert it to posix time.
84 const MYSQL_TIME* database_time = reinterpret_cast<const MYSQL_TIME*>(&buffer_[0]);
85 return (convertFromDatabaseTime(*database_time));
86}
87
88ptime
89MySqlBinding::getTimestampOrDefault(const ptime& default_value) const {
90 if (amNull()) {
91 return (default_value);
92 }
93 return (getTimestamp());
94}
95
97MySqlBinding::createString(const unsigned long length) {
99 length));
100 return (binding);
101}
102
104MySqlBinding::createString(const std::string& value) {
106 value.size()));
107 binding->setBufferValue(value.begin(), value.end());
108 return (binding);
109}
110
113 return (value.unspecified() ? MySqlBinding::createNull() : createString(value));
114}
115
117MySqlBinding::createBlob(const unsigned long length) {
118 MySqlBindingPtr binding(new MySqlBinding(MySqlBindingTraits<std::vector<uint8_t> >::column_type,
119 length));
120 return (binding);
121}
122
124MySqlBinding::createFloat(const float value) {
125 // It may seem a bit weird that we use createInteger template method
126 // for setting a floating point value. However, the setInteger method
127 // seems to be generic enough to support it. If we were to redo the
128 // API of this class we would probably introduce a createNumericValue
129 // method instead of createInteger. However, we already have createInteger
130 // used in many places so we should stick to it.
131 return (createInteger<float>(value));
132}
133
135MySqlBinding::createBool(const bool value) {
136 return (createInteger<uint8_t>(static_cast<uint8_t>(value)));
137}
138
141 if (value.unspecified()) {
142 return (MySqlBinding::createNull());
143 }
144
145 return (createInteger<uint8_t>(static_cast<uint8_t>(value.get())));
146}
147
150 // If the value is unspecified it doesn't matter what the value is.
151 if (value.unspecified()) {
152 return (MySqlBinding::createNull());
153 }
154
155 // Make sure it is an IPv4 address.
156 if (!value.get().isV4()) {
157 isc_throw(BadValue, "unable to create a MySQL binding: specified value '"
158 << value.get().toText() << "' is not an IPv4 address");
159 }
160
161 return (createInteger<uint32_t>(value.get().toUint32()));
162}
163
165MySqlBinding::createTimestamp(const boost::posix_time::ptime& timestamp) {
168 binding->setTimestampValue(timestamp);
169 return (binding);
170}
171
176 return (binding);
177}
178
181 MySqlBindingPtr binding(new MySqlBinding(MYSQL_TYPE_NULL, 0));
182 return (binding);
183}
184
185void
187 MYSQL_TIME& output_time) {
188
189 // Clear output data.
190 memset(&output_time, 0, sizeof(MYSQL_TIME));
191
192 // Convert to broken-out time
193 struct tm time_tm;
194 (void) localtime_r(&input_time, &time_tm);
195
196 // Place in output expire structure.
197 output_time.year = time_tm.tm_year + 1900;
198 output_time.month = time_tm.tm_mon + 1; // Note different base
199 output_time.day = time_tm.tm_mday;
200 output_time.hour = time_tm.tm_hour;
201 output_time.minute = time_tm.tm_min;
202 output_time.second = time_tm.tm_sec;
203 output_time.second_part = 0; // No fractional seconds
204 output_time.neg = my_bool(0); // Not negative
205}
206
207void
208MySqlBinding::convertToDatabaseTime(const boost::posix_time::ptime& input_time,
209 MYSQL_TIME& output_time) {
210 if (input_time.is_not_a_date_time()) {
211 isc_throw(BadValue, "Time value is not a valid posix time");
212 }
213
214 // Clear output data.
215 memset(&output_time, 0, sizeof(MYSQL_TIME));
216
217 output_time.year = input_time.date().year();
218 output_time.month = input_time.date().month();
219 output_time.day = input_time.date().day();
220 output_time.hour = input_time.time_of_day().hours();
221 output_time.minute = input_time.time_of_day().minutes();
222 output_time.second = input_time.time_of_day().seconds();
225 output_time.second_part = 0;
226/* output_time.second_part = input_time.time_of_day().fractional_seconds()
227 *1000000/time_duration::ticks_per_second(); */
228 output_time.neg = my_bool(0);
229}
230
231void
233 const uint32_t valid_lifetime,
234 MYSQL_TIME& expire) {
235
236 // Calculate expiry time. Store it in the 64-bit value so as we can detect
237 // overflows.
238 int64_t expire_time_64 = static_cast<int64_t>(cltt) +
239 static_cast<int64_t>(valid_lifetime);
240
241 // Even on 64-bit systems MySQL doesn't seem to accept the timestamps
242 // beyond the max value of int32_t.
243 if (expire_time_64 > DatabaseConnection::MAX_DB_TIME) {
244 isc_throw(BadValue, "Time value is too large: " << expire_time_64);
245 }
246
247 // Clear output data.
248 memset(&expire, 0, sizeof(MYSQL_TIME));
249
250 const time_t expire_time = static_cast<time_t>(expire_time_64);
251
252 // Convert to broken-out time
253 struct tm expire_tm;
254 (void) localtime_r(&expire_time, &expire_tm);
255
256 // Place in output expire structure.
257 expire.year = expire_tm.tm_year + 1900;
258 expire.month = expire_tm.tm_mon + 1; // Note different base
259 expire.day = expire_tm.tm_mday;
260 expire.hour = expire_tm.tm_hour;
261 expire.minute = expire_tm.tm_min;
262 expire.second = expire_tm.tm_sec;
263 expire.second_part = 0; // No fractional seconds
264 expire.neg = my_bool(0); // Not negative
265}
266
267void
269 uint32_t valid_lifetime,
270 time_t& cltt) {
271 // Copy across fields from MYSQL_TIME structure.
272 struct tm expire_tm;
273 memset(&expire_tm, 0, sizeof(expire_tm));
274
275 expire_tm.tm_year = expire.year - 1900;
276 expire_tm.tm_mon = expire.month - 1;
277 expire_tm.tm_mday = expire.day;
278 expire_tm.tm_hour = expire.hour;
279 expire_tm.tm_min = expire.minute;
280 expire_tm.tm_sec = expire.second;
281 expire_tm.tm_isdst = -1; // Let the system work out about DST
282
283 // Convert to local time
284 cltt = mktime(&expire_tm) - valid_lifetime;
285}
286
287ptime
288MySqlBinding::convertFromDatabaseTime(const MYSQL_TIME& database_time) {
291 long fractional = 0;
292 // long fractional = database_time.second_part * time_duration::ticks_per_second()/1000000;
293 ptime pt(boost::gregorian::date(database_time.year,
294 boost::gregorian::greg_month(database_time.month),
295 database_time.day),
296 time_duration(database_time.hour, database_time.minute,
297 database_time.second, fractional));
298
299 return (pt);
300}
301
302MySqlBinding::MySqlBinding(enum_field_types buffer_type,
303 const size_t length)
304 // Make sure that the buffer has non-zero length in case we need to
305 // reference its first element to assign it to the MySQL binding.
306 : buffer_(length > 0 ? length : 1), length_(length),
307 null_value_(buffer_type == MYSQL_TYPE_NULL) {
308 memset(&bind_, 0, sizeof(MYSQL_BIND));
309 bind_.buffer_type = buffer_type;
310
311 if (buffer_type != MYSQL_TYPE_NULL) {
312 bind_.buffer = &buffer_[0];
313 bind_.buffer_length = length_;
314 bind_.length = &length_;
315 bind_.is_null = &null_value_;
316 }
317}
318
319void
320MySqlBinding::setBufferLength(const unsigned long length) {
321 length_ = length;
322 // It appears that the MySQL connectors sometimes require that the
323 // buffer is specified (set to a non-zero value), even if the buffer
324 // length is 0. We have found that setting the buffer to 0 value would
325 // cause the value inserted to the database be NULL. In order to avoid
326 // it, we simply make sure that the buffer length is at least 1 byte and
327 // provide the pointer to this byte within the binding.
328 buffer_.resize(length_ > 0 ? length_ : 1);
329 bind_.buffer = &buffer_[0];
330 bind_.buffer_length = length_;
331}
332
333void
334MySqlBinding::setTimestampValue(const ptime& timestamp) {
335 MYSQL_TIME database_time;
336 convertToDatabaseTime(timestamp, database_time);
337 // Copy database time into the buffer.
338 memcpy(static_cast<void*>(&buffer_[0]), reinterpret_cast<char*>(&database_time),
339 sizeof(MYSQL_TIME));
340 bind_.buffer = &buffer_[0];
341}
342
343} // end of namespace isc::db
344} // end of namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
static const time_t MAX_DB_TIME
Defines maximum value for time that can be reliably stored.
MySQL binding used in prepared statements.
static void convertFromDatabaseTime(const MYSQL_TIME &expire, uint32_t valid_lifetime, time_t &cltt)
Converts Database Time to Lease Times.
static MySqlBindingPtr condCreateBool(const util::Optional< bool > &value)
Conditionally creates binding of uint8_t type representing a boolean value if provided value is speci...
std::vector< uint8_t > getBlobOrDefault(const std::vector< uint8_t > &default_value) const
Returns value held in the binding as blob.
std::vector< uint8_t > getBlob() const
Returns value held in the binding as blob.
static MySqlBindingPtr condCreateString(const util::Optional< std::string > &value)
Conditionally creates binding of text type for sending data if provided value is unspecified.
static MySqlBindingPtr createString(const unsigned long length)
Creates binding of text type for receiving data.
bool amNull() const
Checks if the bound value is NULL.
std::string getString() const
Returns value held in the binding as string.
data::ElementPtr getJSON() const
Returns value held in the binding as JSON.
std::string getStringOrDefault(const std::string &default_value) const
Returns value held in the binding as string.
static MySqlBindingPtr createTimestamp()
Creates binding of timestamp type for receiving data.
static MySqlBindingPtr createFloat(const float value)
Creates binding having a float type for sending data.
float getFloat() const
Returns float value held in the binding.
static MySqlBindingPtr createNull()
Creates binding encapsulating a NULL value.
boost::posix_time::ptime getTimestamp() const
Returns timestamp value held in the binding.
static MySqlBindingPtr createBlob(const unsigned long length)
Creates binding of blob type for receiving data.
static void convertToDatabaseTime(const time_t input_time, MYSQL_TIME &output_time)
Converts time_t value to database time.
static MySqlBindingPtr condCreateIPv4Address(const util::Optional< asiolink::IOAddress > &value)
Conditionally creates binding of uint32_t type representing an IPv4 address if provided value is spec...
static MySqlBindingPtr createBool(const bool value)
Creates binding having a bool type for sending data.
boost::posix_time::ptime getTimestampOrDefault(const boost::posix_time::ptime &default_value) const
Returns timestamp value held in the binding.
A template representing an optional value.
Definition: optional.h:36
T get() const
Retrieves the encapsulated value.
Definition: optional.h:112
void unspecified(bool unspecified)
Modifies the flag that indicates whether the value is specified or unspecified.
Definition: optional.h:121
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< Element > ElementPtr
Definition: data.h:24
boost::shared_ptr< MySqlBinding > MySqlBindingPtr
Shared pointer to the Binding class.
bool my_bool
my_bool type in MySQL 8.x.
Definition: edns.h:19
Defines the logger used by the top-level component of kea-lfc.
Trait class for column types supported in MySQL.
Definition: mysql_binding.h:36