Kea 2.5.9
mysql_binding.cc
Go to the documentation of this file.
1// Copyright (C) 2018-2022 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
136 return (createInteger<uint8_t>(static_cast<uint8_t>(false)));
137}
138
140MySqlBinding::createBool(const bool value) {
141 return (createInteger<uint8_t>(static_cast<uint8_t>(value)));
142}
143
146 if (value.unspecified()) {
147 return (MySqlBinding::createNull());
148 }
149
150 return (createInteger<uint8_t>(static_cast<uint8_t>(value.get())));
151}
152
155 // If the value is unspecified it doesn't matter what the value is.
156 if (value.unspecified()) {
157 return (MySqlBinding::createNull());
158 }
159
160 // Make sure it is an IPv4 address.
161 if (!value.get().isV4()) {
162 isc_throw(BadValue, "unable to create a MySQL binding: specified value '"
163 << value.get().toText() << "' is not an IPv4 address");
164 }
165
166 return (createInteger<uint32_t>(value.get().toUint32()));
167}
168
170MySqlBinding::createTimestamp(const boost::posix_time::ptime& timestamp) {
173 binding->setTimestampValue(timestamp);
174 return (binding);
175}
176
181 return (binding);
182}
183
186 MySqlBindingPtr binding(new MySqlBinding(MYSQL_TYPE_NULL, 0));
187 return (binding);
188}
189
190void
192 MYSQL_TIME& output_time) {
193
194 // Clear output data.
195 memset(&output_time, 0, sizeof(MYSQL_TIME));
196
197 // Convert to broken-out time
198 struct tm time_tm;
199 (void) localtime_r(&input_time, &time_tm);
200
201 // Place in output expire structure.
202 output_time.year = time_tm.tm_year + 1900;
203 output_time.month = time_tm.tm_mon + 1; // Note different base
204 output_time.day = time_tm.tm_mday;
205 output_time.hour = time_tm.tm_hour;
206 output_time.minute = time_tm.tm_min;
207 output_time.second = time_tm.tm_sec;
208 output_time.second_part = 0; // No fractional seconds
209 output_time.neg = my_bool(0); // Not negative
210}
211
212void
213MySqlBinding::convertToDatabaseTime(const boost::posix_time::ptime& input_time,
214 MYSQL_TIME& output_time) {
215 if (input_time.is_not_a_date_time()) {
216 isc_throw(BadValue, "Time value is not a valid posix time");
217 }
218
219 // Clear output data.
220 memset(&output_time, 0, sizeof(MYSQL_TIME));
221
222 output_time.year = input_time.date().year();
223 output_time.month = input_time.date().month();
224 output_time.day = input_time.date().day();
225 output_time.hour = input_time.time_of_day().hours();
226 output_time.minute = input_time.time_of_day().minutes();
227 output_time.second = input_time.time_of_day().seconds();
230 output_time.second_part = 0;
231/* output_time.second_part = input_time.time_of_day().fractional_seconds()
232 *1000000/time_duration::ticks_per_second(); */
233 output_time.neg = my_bool(0);
234}
235
236void
238 const uint32_t valid_lifetime,
239 MYSQL_TIME& expire) {
240
241 // Calculate expiry time. Store it in the 64-bit value so as we can detect
242 // overflows.
243 int64_t expire_time_64 = static_cast<int64_t>(cltt) +
244 static_cast<int64_t>(valid_lifetime);
245
246 // Even on 64-bit systems MySQL doesn't seem to accept the timestamps
247 // beyond the max value of int32_t.
248 if (expire_time_64 > DatabaseConnection::MAX_DB_TIME) {
249 isc_throw(BadValue, "Time value is too large: " << expire_time_64);
250 }
251
252 // Clear output data.
253 memset(&expire, 0, sizeof(MYSQL_TIME));
254
255 const time_t expire_time = static_cast<time_t>(expire_time_64);
256
257 // Convert to broken-out time
258 struct tm expire_tm;
259 (void) localtime_r(&expire_time, &expire_tm);
260
261 // Place in output expire structure.
262 expire.year = expire_tm.tm_year + 1900;
263 expire.month = expire_tm.tm_mon + 1; // Note different base
264 expire.day = expire_tm.tm_mday;
265 expire.hour = expire_tm.tm_hour;
266 expire.minute = expire_tm.tm_min;
267 expire.second = expire_tm.tm_sec;
268 expire.second_part = 0; // No fractional seconds
269 expire.neg = my_bool(0); // Not negative
270}
271
272void
274 uint32_t valid_lifetime,
275 time_t& cltt) {
276 // Copy across fields from MYSQL_TIME structure.
277 struct tm expire_tm;
278 memset(&expire_tm, 0, sizeof(expire_tm));
279
280 expire_tm.tm_year = expire.year - 1900;
281 expire_tm.tm_mon = expire.month - 1;
282 expire_tm.tm_mday = expire.day;
283 expire_tm.tm_hour = expire.hour;
284 expire_tm.tm_min = expire.minute;
285 expire_tm.tm_sec = expire.second;
286 expire_tm.tm_isdst = -1; // Let the system work out about DST
287
288 // Convert to local time
289 cltt = mktime(&expire_tm) - valid_lifetime;
290}
291
292ptime
293MySqlBinding::convertFromDatabaseTime(const MYSQL_TIME& database_time) {
296 long fractional = 0;
297 // long fractional = database_time.second_part * time_duration::ticks_per_second()/1000000;
298 ptime pt(boost::gregorian::date(database_time.year,
299 boost::gregorian::greg_month(database_time.month),
300 database_time.day),
301 time_duration(database_time.hour, database_time.minute,
302 database_time.second, fractional));
303
304 return (pt);
305}
306
307MySqlBinding::MySqlBinding(enum_field_types buffer_type,
308 const size_t length)
309 // Make sure that the buffer has non-zero length in case we need to
310 // reference its first element to assign it to the MySQL binding.
311 : buffer_(length > 0 ? length : 1), length_(length),
312 null_value_(buffer_type == MYSQL_TYPE_NULL) {
313 memset(&bind_, 0, sizeof(MYSQL_BIND));
314 bind_.buffer_type = buffer_type;
315
316 if (buffer_type != MYSQL_TYPE_NULL) {
317 bind_.buffer = &buffer_[0];
318 bind_.buffer_length = length_;
319 bind_.length = &length_;
320 bind_.is_null = &null_value_;
321 }
322}
323
324void
325MySqlBinding::setBufferLength(const unsigned long length) {
326 length_ = length;
327 // It appears that the MySQL connectors sometimes require that the
328 // buffer is specified (set to a non-zero value), even if the buffer
329 // length is 0. We have found that setting the buffer to 0 value would
330 // cause the value inserted to the database be NULL. In order to avoid
331 // it, we simply make sure that the buffer length is at least 1 byte and
332 // provide the pointer to this byte within the binding.
333 buffer_.resize(length_ > 0 ? length_ : 1);
334 bind_.buffer = &buffer_[0];
335 bind_.buffer_length = length_;
336}
337
338void
339MySqlBinding::setTimestampValue(const ptime& timestamp) {
340 MYSQL_TIME database_time;
341 convertToDatabaseTime(timestamp, database_time);
342 // Copy database time into the buffer.
343 memcpy(static_cast<void*>(&buffer_[0]), reinterpret_cast<char*>(&database_time),
344 sizeof(MYSQL_TIME));
345 bind_.buffer = &buffer_[0];
346}
347
348} // end of namespace isc::db
349} // end of namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
static ElementPtr fromJSON(const std::string &in, bool preproc=false)
These functions will parse the given string (JSON) representation of a compound element.
Definition: data.cc:798
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.
static MySqlBindingPtr createBool()
Creates binding having a bool type for receiving data.
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...
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:114
void unspecified(bool unspecified)
Modifies the flag that indicates whether the value is specified or unspecified.
Definition: optional.h:136
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< Element > ElementPtr
Definition: data.h:28
boost::shared_ptr< MySqlBinding > MySqlBindingPtr
Shared pointer to the Binding class.
bool my_bool
my_bool type in MySQL 8.x.
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