1 // Copyright (C) 2018-2019 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
9 #include <asiolink/io_address.h>
10 #include <exceptions/exceptions.h>
11 #include <mysql/mysql_binding.h>
12 #include <util/optional.h>
13 #include <boost/date_time/gregorian/gregorian.hpp>
14 #include <boost/date_time/posix_time/posix_time.hpp>
15 #include <gtest/gtest.h>
16
17 using namespace isc;
18 using namespace isc::asiolink;
19 using namespace isc::data;
20 using namespace isc::db;
21 using namespace isc::util;
22
23 namespace {
24
25 // This test verifies that default string is returned if binding is null.
TEST(MySqlBindingTest,defaultString)26 TEST(MySqlBindingTest, defaultString) {
27 auto binding = MySqlBinding::createNull();
28 EXPECT_EQ("foo", binding->getStringOrDefault("foo"));
29
30 binding = MySqlBinding::createString("bar");
31 ASSERT_FALSE(binding->amNull());
32 EXPECT_EQ("bar", binding->getStringOrDefault("foo"));
33 }
34
35 // This test verifies that null binding is created for unspecified string
36 // and the string binding is created for a specified string.
TEST(MySqlBindingTest,conditionalString)37 TEST(MySqlBindingTest, conditionalString) {
38 auto binding = MySqlBinding::condCreateString(Optional<std::string>());
39 EXPECT_TRUE(binding->amNull());
40
41 binding = MySqlBinding::condCreateString("foo");
42 ASSERT_FALSE(binding->amNull());
43 EXPECT_EQ("foo", binding->getString());
44 }
45
46 // This test verifies that empty string is stored in the database.
TEST(MySqlBindingTest,emptyString)47 TEST(MySqlBindingTest, emptyString) {
48 auto binding = MySqlBinding::condCreateString(Optional<std::string>(""));
49 ASSERT_FALSE(binding->amNull());
50 EXPECT_TRUE(binding->getString().empty());
51 }
52
53 // This test verifies that an error is thrown upon an attempt to use
54 // invalid accessor for a string binding.
TEST(MySqlBindingTest,stringTypeMismatch)55 TEST(MySqlBindingTest, stringTypeMismatch) {
56 auto binding = MySqlBinding::createString("foo");
57 EXPECT_NO_THROW(static_cast<void>(binding->getString()));
58
59 EXPECT_THROW(static_cast<void>(binding->getBlob()), InvalidOperation);
60 EXPECT_THROW(static_cast<void>(binding->getInteger<uint16_t>()), InvalidOperation);
61 EXPECT_THROW(static_cast<void>(binding->getFloat()), InvalidOperation);
62 EXPECT_THROW(static_cast<void>(binding->getBool()), InvalidOperation);
63 EXPECT_THROW(static_cast<void>(binding->getTimestamp()), InvalidOperation);
64 }
65
66 // This test verifies that null JSON is returned if the string binding
67 // is null, JSON value is returned when string value is valid JSON and
68 // that exception is thrown if the string is not a valid JSON.
TEST(MySqlBindingTest,getJSON)69 TEST(MySqlBindingTest, getJSON) {
70 auto binding = MySqlBinding::createNull();
71 EXPECT_FALSE(binding->getJSON());
72
73 binding = MySqlBinding::createString("{ \"foo\": \"bar\" }");
74 auto json = binding->getJSON();
75 ASSERT_TRUE(json);
76 ASSERT_EQ(Element::map, json->getType());
77 auto foo = json->get("foo");
78 ASSERT_TRUE(foo);
79 ASSERT_EQ(Element::string, foo->getType());
80 EXPECT_EQ("bar", foo->stringValue());
81 }
82
83 // This test verifies that default blob is returned if binding is null.
TEST(MySqlBindingTest,defaultBlob)84 TEST(MySqlBindingTest, defaultBlob) {
85 std::vector<uint8_t> blob(10, 1);
86 std::vector<uint8_t> default_blob(10, 5);
87 auto binding = MySqlBinding::createNull();
88 EXPECT_EQ(default_blob, binding->getBlobOrDefault(default_blob));
89
90 binding = MySqlBinding::createBlob(blob.begin(), blob.end());
91 EXPECT_EQ(blob, binding->getBlobOrDefault(default_blob));
92 }
93
94 // This test verifies that an error is thrown upon an attempt to use
95 // invalid accessor for a blob binding.
TEST(MySqlBindingTest,blobTypeMismatch)96 TEST(MySqlBindingTest, blobTypeMismatch) {
97 std::vector<uint8_t> blob(10, 1);
98 auto binding = MySqlBinding::createBlob(blob.begin(), blob.end());
99 EXPECT_NO_THROW(static_cast<void>(binding->getBlob()));
100
101 EXPECT_THROW(static_cast<void>(binding->getString()), InvalidOperation);
102 EXPECT_THROW(static_cast<void>(binding->getInteger<uint16_t>()), InvalidOperation);
103 EXPECT_THROW(static_cast<void>(binding->getFloat()), InvalidOperation);
104 EXPECT_THROW(static_cast<void>(binding->getBool()), InvalidOperation);
105 EXPECT_THROW(static_cast<void>(binding->getTimestamp()), InvalidOperation);
106 }
107
108 // This test verifies that default number is returned if binding is null.
TEST(MySqlBindingTest,defaultInteger)109 TEST(MySqlBindingTest, defaultInteger) {
110 auto binding = MySqlBinding::createNull();
111 ASSERT_TRUE(binding->amNull());
112 EXPECT_EQ(123, binding->getIntegerOrDefault<uint32_t>(123));
113
114 binding = MySqlBinding::createInteger<uint32_t>(1024);
115 ASSERT_FALSE(binding->amNull());
116 EXPECT_EQ(1024, binding->getIntegerOrDefault<uint32_t>(123));
117 }
118
119 // This test verifies that null binding is created for unspecified number
120 // and the integer binding is created for a specified number.
TEST(MySqlBindingTest,conditionalInteger)121 TEST(MySqlBindingTest, conditionalInteger) {
122 auto binding = MySqlBinding::condCreateInteger<uint16_t>(Optional<uint16_t>());
123 EXPECT_TRUE(binding->amNull());
124
125 binding = MySqlBinding::condCreateInteger<uint16_t>(1);
126 ASSERT_FALSE(binding->amNull());
127 EXPECT_EQ(1, binding->getInteger<uint16_t>());
128 }
129
130 // This test verifies that an error is thrown upon an attempt to use
131 // invalid accessor for an integer binding.
TEST(MySqlBindingTest,integerTypeMismatch)132 TEST(MySqlBindingTest, integerTypeMismatch) {
133 auto binding = MySqlBinding::createInteger<uint32_t>(123);
134 EXPECT_NO_THROW(static_cast<void>(binding->getInteger<uint32_t>()));
135
136 EXPECT_THROW(static_cast<void>(binding->getString()), InvalidOperation);
137 EXPECT_THROW(static_cast<void>(binding->getBlob()), InvalidOperation);
138 EXPECT_THROW(static_cast<void>(binding->getInteger<uint8_t>()), InvalidOperation);
139 EXPECT_THROW(static_cast<void>(binding->getInteger<uint16_t>()), InvalidOperation);
140 EXPECT_THROW(static_cast<void>(binding->getFloat()), InvalidOperation);
141 EXPECT_THROW(static_cast<void>(binding->getBool()), InvalidOperation);
142 EXPECT_THROW(static_cast<void>(binding->getTimestamp()), InvalidOperation);
143 }
144
145 // This test verifies that null binding is created for unspecified floating
146 // point value and the float binding is created for the specified value.
TEST(MySqlBindingTest,conditionalFloat)147 TEST(MySqlBindingTest, conditionalFloat) {
148 auto binding = MySqlBinding::condCreateFloat(Optional<float>());
149 EXPECT_TRUE(binding->amNull());
150
151 binding = MySqlBinding::condCreateFloat<float>(1.567f);
152 ASSERT_FALSE(binding->amNull());
153 EXPECT_EQ(1.567f, binding->getFloat());
154 }
155
156 // This test verifies that an error is thrown upon an attempt to use
157 // invalid accessor for a float binding.
TEST(MySqlBindingTest,floatTypeMismatch)158 TEST(MySqlBindingTest, floatTypeMismatch) {
159 auto binding = MySqlBinding::createFloat(123.123f);
160 EXPECT_NO_THROW(static_cast<void>(binding->getFloat()));
161
162 EXPECT_THROW(static_cast<void>(binding->getString()), InvalidOperation);
163 EXPECT_THROW(static_cast<void>(binding->getBlob()), InvalidOperation);
164 EXPECT_THROW(static_cast<void>(binding->getInteger<uint8_t>()), InvalidOperation);
165 EXPECT_THROW(static_cast<void>(binding->getInteger<uint16_t>()), InvalidOperation);
166 EXPECT_THROW(static_cast<void>(binding->getInteger<uint32_t>()), InvalidOperation);
167 EXPECT_THROW(static_cast<void>(binding->getBool()), InvalidOperation);
168 EXPECT_THROW(static_cast<void>(binding->getTimestamp()), InvalidOperation);
169 }
170
171 // This test verifies that null binding is created for unspecified boolean
172 // value and the uint8_t binding is created for a specified boolean
173 // value.
TEST(MySqlBindingTest,conditionalBoolean)174 TEST(MySqlBindingTest, conditionalBoolean) {
175 auto binding = MySqlBinding::condCreateBool(Optional<bool>());
176 EXPECT_TRUE(binding->amNull());
177
178 binding = MySqlBinding::condCreateBool(false);
179 ASSERT_FALSE(binding->amNull());
180 EXPECT_FALSE(binding->getBool());
181
182 binding = MySqlBinding::condCreateBool(true);
183 ASSERT_FALSE(binding->amNull());
184 EXPECT_TRUE(binding->getBool());
185 }
186
187 // This test verifies that an error is thrown upon an attempt to use
188 // invalid accessor for a float binding.
TEST(MySqlBindingTest,booleanTypeMismatch)189 TEST(MySqlBindingTest, booleanTypeMismatch) {
190 auto binding = MySqlBinding::createBool(false);
191 EXPECT_NO_THROW(static_cast<void>(binding->getBool()));
192 EXPECT_NO_THROW(static_cast<void>(binding->getInteger<uint8_t>()));
193
194 EXPECT_THROW(static_cast<void>(binding->getString()), InvalidOperation);
195 EXPECT_THROW(static_cast<void>(binding->getBlob()), InvalidOperation);
196 EXPECT_THROW(static_cast<void>(binding->getInteger<uint16_t>()), InvalidOperation);
197 EXPECT_THROW(static_cast<void>(binding->getInteger<uint32_t>()), InvalidOperation);
198 EXPECT_THROW(static_cast<void>(binding->getFloat()), InvalidOperation);
199 EXPECT_THROW(static_cast<void>(binding->getTimestamp()), InvalidOperation);
200 }
201
202 // This test verifies that null binding is created for unspecified address
203 // and the uint32_t binding is created for the specified address.
TEST(MySqlBindingTest,conditionalIPv4Address)204 TEST(MySqlBindingTest, conditionalIPv4Address) {
205 auto binding = MySqlBinding::condCreateIPv4Address(Optional<IOAddress>());
206 EXPECT_TRUE(binding->amNull());
207
208 binding = MySqlBinding::condCreateIPv4Address(IOAddress("192.0.2.1"));
209 ASSERT_FALSE(binding->amNull());
210 EXPECT_EQ(0xC0000201, binding->getInteger<uint32_t>());
211
212 EXPECT_THROW(MySqlBinding::condCreateIPv4Address(IOAddress("2001:db8:1::1")),
213 isc::BadValue);
214 }
215
216 // This test verifies that default timestamp is returned if binding is null.
TEST(MySqlBindingTest,defaultTimestamp)217 TEST(MySqlBindingTest, defaultTimestamp) {
218 boost::posix_time::ptime current_time = boost::posix_time::second_clock::local_time();
219 boost::posix_time::ptime past_time = current_time - boost::posix_time::hours(1);
220
221 auto binding = MySqlBinding::createNull();
222 EXPECT_TRUE(past_time == binding->getTimestampOrDefault(past_time));
223
224 binding = MySqlBinding::createTimestamp(current_time);
225 EXPECT_TRUE(current_time == binding->getTimestampOrDefault(past_time));
226 }
227
228 // This test verifies that the binding preserves fractional seconds in
229 // millisecond precision.
230 /// @todo This test is disabled until we decide that the minimum
231 /// supported MySQL version has a fractional seconds precision.
TEST(MySqlBindingTest,DISABLED_millisecondTimestampPrecision)232 TEST(MySqlBindingTest, DISABLED_millisecondTimestampPrecision) {
233 // Set timestamp of 2019-01-28 01:12:10.123
234
235 // Fractional part depends on the clock resolution.
236 long fractional = 123*(boost::posix_time::time_duration::ticks_per_second()/1000);
237 boost::posix_time::ptime
238 test_time(boost::gregorian::date(2019, boost::gregorian::Jan, 28),
239 boost::posix_time::time_duration(1, 12, 10, fractional));
240
241 auto binding = MySqlBinding::createTimestamp(test_time);
242
243 boost::posix_time::ptime returned_test_time = binding->getTimestamp();
244
245 EXPECT_EQ(returned_test_time, test_time);
246 }
247
248 }
249
250