1 /*
2  * Copyright (c) 2015, 2021, Oracle and/or its affiliates.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License, version 2.0,
6  * as published by the Free Software Foundation.
7  *
8  * This program is also distributed with certain software (including
9  * but not limited to OpenSSL) that is licensed under separate terms,
10  * as designated in a particular file or component or in included license
11  * documentation.  The authors of MySQL hereby grant you an additional
12  * permission to link the program and your derivative works with the
13  * separately licensed software that they have included with MySQL.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License, version 2.0, for more details.
19  *
20  * You should have received a copy of the GNU General Public License along
21  * with this program; if not, write to the Free Software Foundation, Inc.,
22  * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23  */
24 
25 #include "ngs/error_code.h"
26 #include "query_string_builder.h"
27 
28 #include <stdexcept>
29 #include <list>
30 #include <gtest/gtest.h>
31 
32 
33 namespace xpl {
34 
35 namespace test {
36 
37 namespace {
38 
39 class Assign_list {
40 public:
41   typedef std::string Value_type;
42   typedef std::list<Value_type> Container_type;
43 
Assign_list(const Value_type & value)44   Assign_list(const Value_type &value) {
45     (*this)(value);
46   }
47 
operator ()(const Value_type & value)48   Assign_list &operator() (const Value_type &value) {
49     m_values.push_back(value);
50     return *this;
51   }
52 
operator Container_type()53   operator Container_type() {
54     return m_values;
55   }
56 
57 private:
58   Container_type m_values;
59 };
60 
61 } // namespace
62 
63 
64 class Query_string_builder_testsuite : public ::testing::Test {
65 public:
66   Query_string_builder query;
67 };
68 
TEST_F(Query_string_builder_testsuite,format_doesNothing_whenEmptyStringAndNoArgsPushed)69 TEST_F(Query_string_builder_testsuite, format_doesNothing_whenEmptyStringAndNoArgsPushed) {
70   query.format();
71 
72   ASSERT_STREQ("", query.get().c_str());
73 }
74 
TEST_F(Query_string_builder_testsuite,format_doesThrowsException_whenEmptyFormat)75 TEST_F(Query_string_builder_testsuite, format_doesThrowsException_whenEmptyFormat) {
76   ASSERT_THROW(query.format() % "Test", ngs::Error_code);
77 
78   ASSERT_STREQ("", query.get().c_str());
79 }
80 
TEST_F(Query_string_builder_testsuite,format_doesThrowsException_whenNoTagFormat)81 TEST_F(Query_string_builder_testsuite, format_doesThrowsException_whenNoTagFormat) {
82   const char *format_query = "QUERY WITHOUT TAG";
83 
84   query.put(format_query);
85   ASSERT_THROW(query.format() % "Test", ngs::Error_code);
86 
87   ASSERT_STREQ(format_query, query.get().c_str());
88 }
89 
TEST_F(Query_string_builder_testsuite,format_doesThrowsException_whenAfterFillingFirstTag)90 TEST_F(Query_string_builder_testsuite, format_doesThrowsException_whenAfterFillingFirstTag) {
91   query.put("SELECT ? FROM table").format() % "test";
92 
93   ASSERT_THROW(query.format() % "test1", ngs::Error_code);
94   ASSERT_THROW(query.format() % "test2", ngs::Error_code);
95 
96   ASSERT_STREQ("SELECT 'test' FROM table", query.get().c_str());
97 }
98 
TEST_F(Query_string_builder_testsuite,format_fillsTwoTagsInQueryAndSkipsTagInValue_whenTwoTagValue)99 TEST_F(Query_string_builder_testsuite, format_fillsTwoTagsInQueryAndSkipsTagInValue_whenTwoTagValue) {
100   query.put("SELECT ? FROM ?");
101   query.format() % "?" % "test";
102 
103   ASSERT_STREQ("SELECT '?' FROM 'test'", query.get().c_str());
104 }
105 
TEST_F(Query_string_builder_testsuite,format_fillNumericValues)106 TEST_F(Query_string_builder_testsuite, format_fillNumericValues) {
107   query.put("SELECT *,? FROM t WHERE x=?").format() % 1 % 1.1;
108 
109   ASSERT_STREQ("SELECT *,1 FROM t WHERE x=1.1", query.get().c_str());
110 }
111 
112 struct Query_and_expected {
Query_and_expectedxpl::test::Query_and_expected113   Query_and_expected(std::string query, std::string expected, std::string value)
114   : m_query(query), m_expected(expected), m_value(value) {
115   }
116 
117   std::string m_query;
118   std::string m_expected;
119   std::string m_value;
120 };
121 
operator <<(::std::ostream & os,const Query_and_expected & query_and_expected)122 ::std::ostream& operator<<(::std::ostream& os, const Query_and_expected& query_and_expected) {
123   return os << "Query:" << query_and_expected.m_query << " expected:" << query_and_expected.m_expected << " value:" << query_and_expected.m_value << std::endl;
124 }
125 
126 class Query_string_builder_param_testsuite :
127     public Query_string_builder_testsuite,
128     public ::testing::WithParamInterface<Query_and_expected> {
129 public:
130 };
131 
TEST_P(Query_string_builder_param_testsuite,format_putStringValueInsideQuery_whenOneTagInFormat)132 TEST_P(Query_string_builder_param_testsuite, format_putStringValueInsideQuery_whenOneTagInFormat) {
133   const char *value = GetParam().m_value.c_str();
134   const char *format_query = GetParam().m_query.c_str();
135   const char *expected_query = GetParam().m_expected.c_str();
136 
137   query.put(format_query).format() % value;
138 
139   ASSERT_STREQ(expected_query, query.get().c_str());
140 }
141 
142 INSTANTIATE_TEST_CASE_P(InstantiationPositiveTest,
143     Query_string_builder_param_testsuite,
144     ::testing::Values(Query_and_expected("SELECT ? FROM", "SELECT 'Test' FROM", "Test"),
145                       Query_and_expected("SELECT Next,?", "SELECT Next,'FROM'", "FROM"),
146                       Query_and_expected("?,Back,FROM",   "'Select',Back,FROM", "Select"),
147                       Query_and_expected("SELECT Next ?", "SELECT Next ''",     ""),
148                       Query_and_expected("SELECT ? From", "SELECT '' From",     ""),
149                       Query_and_expected("? From",        "'' From",            ""),
150                       Query_and_expected("? From",        "'\\'first-word\\' ; \\\"second-word\\\"' From" , "'first-word' ; \"second-word\"")
151                       ));
152 
153 struct Query_and_expected_values {
Query_and_expected_valuesxpl::test::Query_and_expected_values154   Query_and_expected_values(std::string query, std::string expected, std::list<std::string> values)
155   : m_query(query),
156     m_expected(expected),
157     m_values(values) {
158   }
159 
160   std::string m_query;
161   std::string m_expected;
162   std::list<std::string> m_values;
163 };
164 
operator <<(::std::ostream & os,const Query_and_expected_values & query_and_expected)165 ::std::ostream& operator<<(::std::ostream& os, const Query_and_expected_values& query_and_expected) {
166   os << "Query:" << query_and_expected.m_query << " expected:" << query_and_expected.m_expected << std::endl << " [";
167 
168   std::ostream_iterator<std::string> out_it (os,", ");
169   std::copy(query_and_expected.m_values.begin(), query_and_expected.m_values.end(), out_it);
170 
171   return os << "]" << std::endl;
172 }
173 
174 class Query_string_builder_multiple_tags_param_testsuite :
175     public Query_string_builder_testsuite,
176     public ::testing::WithParamInterface<Query_and_expected_values> {
177 public:
SetUp()178   void SetUp() {
179     values = GetParam().m_values;
180     expected_query = &GetParam().m_expected[0];
181 
182     query.put(GetParam().m_query.c_str());
183   }
184 
185   std::list<std::string> values;
186   std::string            expected_query;
187 };
188 
TEST_P(Query_string_builder_multiple_tags_param_testsuite,format_putStringValueInsideQuery_whenMultipleTagsInFormat)189 TEST_P(Query_string_builder_multiple_tags_param_testsuite, format_putStringValueInsideQuery_whenMultipleTagsInFormat) {
190   std::list<std::string>::const_iterator i = values.begin();
191 
192   // In each iteration format creates new object without previous state
193   // Thus similar test are needed with one call/multiple args
194   for (;i!= values.end(); ++i)
195     query.format() % *i;
196 
197   ASSERT_STREQ(expected_query.c_str(), query.get().c_str());
198 }
199 
200 INSTANTIATE_TEST_CASE_P(InstantiationPositiveTests,
201     Query_string_builder_multiple_tags_param_testsuite,
202     ::testing::Values(
203         Query_and_expected_values("SELECT ?,?,? FROM", "SELECT 'First','Second','Third' FROM", Assign_list("First")("Second")("Third")),
204         Query_and_expected_values("SELECT ?,? FROM",   "SELECT 'First','Second' FROM",         Assign_list("First")("Second")),
205         Query_and_expected_values("SELECT ? FROM ?",   "SELECT 'Second' FROM 'First'",         Assign_list("Second")("First")),
206         Query_and_expected_values("SELECT ?? FROM t",  "SELECT 'Second''First' FROM t",        Assign_list("Second")("First")),
207         Query_and_expected_values("SELECT ? FROM ?",               "SELECT '?' FROM 'First'",                  Assign_list("?")("First"))
208         ));
209 
210 INSTANTIATE_TEST_CASE_P(InstantiationPositiveTestsQueryOrValuesWithEscapedChars,
211     Query_string_builder_multiple_tags_param_testsuite,
212     ::testing::Values(
213         Query_and_expected_values("SELECT ?/*,?*/ FROM t WHERE ?", "SELECT '\\\"'/*,?*/ FROM t WHERE 'First'", Assign_list("\"")("First")),
214         Query_and_expected_values("SELECT ?,? FROM t", "SELECT '\\'','First' FROM t",          Assign_list("'")("First")),
215         Query_and_expected_values("SELECT ?,? FROM t", "SELECT '\\\"','First' FROM t",         Assign_list("\"")("First")),
216         Query_and_expected_values("SELECT \"?\'?'?\",?,? FROM t",  "SELECT \"?\'?'?\",'','First' FROM t",      Assign_list("")("First"))
217         ));
218 
219 INSTANTIATE_TEST_CASE_P(InstantiationPositiveTestsQueryWithComment,
220     Query_string_builder_multiple_tags_param_testsuite,
221     ::testing::Values(
222         Query_and_expected_values("SELECT ?/*,?*/ FROM t WHERE ?", "SELECT '\\\"'/*,?*/ FROM t WHERE 'First'", Assign_list("\"")("First")),
223         Query_and_expected_values("SELECT ?#,?*\n FROM t WHERE ?", "SELECT '\\\"'#,?*\n FROM t WHERE 'First'", Assign_list("\"")("First")),
224         Query_and_expected_values("SELECT ?-- ,?*\n FROM t WHERE ?", "SELECT '\\\"'-- ,?*\n FROM t WHERE 'First'", Assign_list("\"")("First")),
225         Query_and_expected_values("SELECT ?-- ,?*\n FROM -- ?\nt WHERE ?", "SELECT '\\\"'-- ,?*\n FROM -- ?\nt WHERE 'First'", Assign_list("\"")("First")),
226         Query_and_expected_values("SELECT ?-- ,?*\n FROM # ?\nt WHERE ?", "SELECT '\\\"'-- ,?*\n FROM # ?\nt WHERE 'First'", Assign_list("\"")("First")),
227         Query_and_expected_values("SELECT ?-- ,?*\n FROM /* ?*/t WHERE ?", "SELECT '\\\"'-- ,?*\n FROM /* ?*/t WHERE 'First'", Assign_list("\"")("First"))
228         ));
229 
230 class Query_string_builder_multiple_too_many_tags_param_testsuite : public Query_string_builder_multiple_tags_param_testsuite {};
231 
TEST_P(Query_string_builder_multiple_too_many_tags_param_testsuite,format_putStringValueInsideQuery_whenMultipleTagsInFormat)232 TEST_P(Query_string_builder_multiple_too_many_tags_param_testsuite, format_putStringValueInsideQuery_whenMultipleTagsInFormat) {
233   std::list<std::string>::const_iterator i = values.begin();
234 
235   ASSERT_LT(0u, values.size());
236 
237   // In each iteration format creates new object without previous state
238   // Thus similar test are needed with one call/multiple args
239   for (;&*i != &values.back(); ++i)
240     query.format() % *i;
241 
242   ASSERT_THROW(query.format() % values.back(), ngs::Error_code);
243 }
244 
245 INSTANTIATE_TEST_CASE_P(InstantiationNegativeTest,
246     Query_string_builder_multiple_too_many_tags_param_testsuite,
247     ::testing::Values(
248         Query_and_expected_values("SELECT ?,? FROM", "", Assign_list("First")("Second")("Third")),
249         Query_and_expected_values("SELECT ? FROM", "", Assign_list("First")("Second"))
250         ));
251 
252 
253 } // namespace test
254 
255 } // namespace xpl
256