Line data Source code
1 : ///////////////////////////////////////////////////////////////////////////////
2 : //
3 : // Project: C++ Test Suite for GDAL/OGR
4 : // Purpose: Test SWQ (SQL WHERE Query) features.
5 : // Author: Even Rouault <even.rouault at spatialys.com>
6 : //
7 : ///////////////////////////////////////////////////////////////////////////////
8 : // Copyright (c) 2023, Even Rouault <even.rouault at spatialys.com>
9 : /*
10 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "gdal_unit_test.h"
30 :
31 : #include "ogr_core.h"
32 : #include "ogr_geometry.h"
33 : #include "ogr_swq.h"
34 :
35 : #include "gtest_include.h"
36 :
37 : namespace
38 : {
39 :
40 : struct test_ogr_swq : public ::testing::Test
41 : {
42 : };
43 :
44 4 : TEST_F(test_ogr_swq, basic)
45 : {
46 : std::vector<swq_expr_node> nodes = {
47 : swq_expr_node(),
48 : swq_expr_node(1),
49 : swq_expr_node(2),
50 : swq_expr_node(1.5),
51 : swq_expr_node(2.5),
52 : swq_expr_node(static_cast<GIntBig>(4000) * 1000 * 1000),
53 : swq_expr_node(static_cast<GIntBig>(4000) * 1000 * 1000 + 1),
54 : swq_expr_node(static_cast<const char *>(nullptr)),
55 : swq_expr_node("a"),
56 : swq_expr_node("b"),
57 : swq_expr_node(SWQ_OR),
58 : swq_expr_node(SWQ_NOT),
59 : swq_expr_node(static_cast<OGRGeometry *>(nullptr)),
60 1 : swq_expr_node(std::make_unique<OGRPoint>(1, 2).get()),
61 2 : swq_expr_node(std::make_unique<OGRPoint>(1, 3).get()),
62 21 : };
63 : {
64 2 : auto node = swq_expr_node(SWQ_NOT);
65 1 : node.PushSubExpression(new swq_expr_node(1));
66 1 : nodes.emplace_back(node);
67 : }
68 : {
69 2 : auto node = swq_expr_node(SWQ_NOT);
70 1 : node.PushSubExpression(new swq_expr_node(2));
71 1 : nodes.emplace_back(node);
72 : }
73 : {
74 2 : auto node = swq_expr_node();
75 1 : node.eNodeType = SNT_COLUMN;
76 1 : node.field_index = 0;
77 1 : node.table_index = 0;
78 1 : nodes.emplace_back(node);
79 : }
80 : {
81 2 : auto node = swq_expr_node();
82 1 : node.eNodeType = SNT_COLUMN;
83 1 : node.field_index = 0;
84 1 : node.table_index = 0;
85 1 : node.table_name = CPLStrdup("foo");
86 1 : nodes.emplace_back(node);
87 : }
88 : {
89 2 : auto node = swq_expr_node();
90 1 : node.eNodeType = SNT_COLUMN;
91 1 : node.field_index = 0;
92 1 : node.table_index = 0;
93 1 : node.table_name = CPLStrdup("bar");
94 1 : nodes.emplace_back(node);
95 : }
96 : {
97 2 : auto node = swq_expr_node();
98 1 : node.eNodeType = SNT_COLUMN;
99 1 : node.field_index = 1;
100 1 : node.table_index = 0;
101 1 : nodes.emplace_back(node);
102 : }
103 : {
104 2 : auto node = swq_expr_node();
105 1 : node.eNodeType = SNT_COLUMN;
106 1 : node.field_index = 0;
107 1 : node.table_index = 1;
108 1 : nodes.emplace_back(node);
109 : }
110 :
111 23 : for (const auto &node1 : nodes)
112 : {
113 506 : for (const auto &node2 : nodes)
114 : {
115 484 : if (&node1 == &node2)
116 : {
117 22 : EXPECT_TRUE(node1 == node1);
118 22 : EXPECT_TRUE(node1 == swq_expr_node(node1));
119 : }
120 : else
121 : {
122 462 : EXPECT_FALSE(node1 == node2);
123 462 : EXPECT_FALSE(node2 == node1);
124 :
125 : {
126 924 : swq_expr_node copy(node1);
127 462 : copy = node2;
128 462 : EXPECT_TRUE(copy == node2);
129 : }
130 : {
131 924 : swq_expr_node copy1(node1);
132 924 : swq_expr_node copy2(node2);
133 462 : copy1 = std::move(copy2);
134 462 : EXPECT_TRUE(copy1 == node2);
135 : }
136 : }
137 : }
138 : }
139 1 : }
140 :
141 : class PushNotOperationDownToStackFixture
142 : : public test_ogr_swq,
143 : public ::testing::WithParamInterface<
144 : std::tuple<const char *, const char *>>
145 : {
146 : public:
147 1 : static std::vector<std::tuple<const char *, const char *>> GetTupleValues()
148 : {
149 : return {
150 : std::make_tuple("NOT(1 = 2)", "1 <> 2"),
151 : std::make_tuple("NOT(1 <> 2)", "1 = 2"),
152 : std::make_tuple("NOT(1 >= 2)", "1 < 2"),
153 : std::make_tuple("NOT(1 > 2)", "1 <= 2"),
154 : std::make_tuple("NOT(1 <= 2)", "1 > 2"),
155 : std::make_tuple("NOT(1 < 2)", "1 >= 2"),
156 : std::make_tuple("NOT(NOT(1))", "1"),
157 : std::make_tuple("NOT(1 AND 2)", "(NOT (1)) OR (NOT (2))"),
158 : std::make_tuple("NOT(1 OR 2)", "(NOT (1)) AND (NOT (2))"),
159 : std::make_tuple("3 AND NOT(1 OR 2)",
160 : "3 AND ((NOT (1)) AND (NOT (2)))"),
161 : std::make_tuple("NOT(NOT(1 = 2) OR 2)", "(1 = 2) AND (NOT (2))"),
162 : std::make_tuple("1", "1"),
163 1 : };
164 : }
165 : };
166 :
167 25 : TEST_P(PushNotOperationDownToStackFixture, test)
168 : {
169 12 : const char *pszInput = std::get<0>(GetParam());
170 12 : const char *pszExpected = std::get<1>(GetParam());
171 :
172 12 : swq_expr_node *poNode = nullptr;
173 12 : swq_expr_compile(pszInput, 0, nullptr, nullptr, true, nullptr, &poNode);
174 12 : ASSERT_TRUE(poNode);
175 12 : poNode->PushNotOperationDownToStack();
176 12 : char *pszStr = poNode->Unparse(nullptr, '"');
177 24 : std::string osStr = pszStr ? pszStr : "";
178 12 : CPLFree(pszStr);
179 12 : EXPECT_STREQ(osStr.c_str(), pszExpected);
180 12 : delete poNode;
181 : }
182 :
183 26 : INSTANTIATE_TEST_SUITE_P(
184 : test_ogr_swq, PushNotOperationDownToStackFixture,
185 : ::testing::ValuesIn(PushNotOperationDownToStackFixture::GetTupleValues()),
186 : [](const ::testing::TestParamInfo<
187 : PushNotOperationDownToStackFixture::ParamType> &l_info)
188 : {
189 : CPLString osStr = std::get<0>(l_info.param);
190 : osStr.replaceAll(' ', '_');
191 : osStr.replaceAll('(', '_');
192 : osStr.replaceAll(')', '_');
193 : osStr.replaceAll("<>", "NE");
194 : osStr.replaceAll(">=", "GE");
195 : osStr.replaceAll(">", "GT");
196 : osStr.replaceAll("<=", "LE");
197 : osStr.replaceAll("<", "LT");
198 : osStr.replaceAll('=', "EQ");
199 : osStr.replaceAll("__", '_');
200 : if (osStr.back() == '_')
201 : osStr.pop_back();
202 : return osStr;
203 : });
204 :
205 4 : TEST_F(test_ogr_swq, select_unparse)
206 : {
207 : {
208 2 : swq_select select;
209 1 : const char *pszSQL = "SELECT a FROM FOO";
210 1 : EXPECT_EQ(select.preparse(pszSQL), CE_None);
211 1 : char *ret = select.Unparse();
212 1 : EXPECT_STREQ(ret, pszSQL);
213 1 : CPLFree(ret);
214 : }
215 : {
216 2 : swq_select select;
217 1 : const char *pszSQL =
218 : "SELECT DISTINCT a, \"a b\" AS renamed, AVG(x.a) AS avg, MIN(a), "
219 : "MAX(\"a b\"), SUM(a), AVG(a), COUNT(a), COUNT(DISTINCT a) "
220 : "FROM 'foo'.\"FOO BAR\" AS x "
221 : "JOIN 'bar'.BAR AS y ON FOO.x = BAR.y "
222 : "WHERE 1 ORDER BY a, \"a b\" DESC "
223 : "LIMIT 1 OFFSET 2";
224 1 : EXPECT_EQ(select.preparse(pszSQL), CE_None);
225 1 : char *ret = select.Unparse();
226 1 : EXPECT_STREQ(ret, pszSQL);
227 1 : CPLFree(ret);
228 : }
229 1 : }
230 :
231 : } // namespace
|