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 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "gdal_unit_test.h"
14 :
15 : #include "ogr_core.h"
16 : #include "ogr_geometry.h"
17 : #include "ogr_swq.h"
18 :
19 : #include "gtest_include.h"
20 :
21 : namespace
22 : {
23 :
24 : struct test_ogr_swq : public ::testing::Test
25 : {
26 : };
27 :
28 4 : TEST_F(test_ogr_swq, basic)
29 : {
30 : std::vector<swq_expr_node> nodes = {
31 : swq_expr_node(),
32 : swq_expr_node(1),
33 : swq_expr_node(2),
34 : swq_expr_node(1.5),
35 : swq_expr_node(2.5),
36 : swq_expr_node(static_cast<GIntBig>(4000) * 1000 * 1000),
37 : swq_expr_node(static_cast<GIntBig>(4000) * 1000 * 1000 + 1),
38 : swq_expr_node(static_cast<const char *>(nullptr)),
39 : swq_expr_node("a"),
40 : swq_expr_node("b"),
41 : swq_expr_node(SWQ_OR),
42 : swq_expr_node(SWQ_NOT),
43 : swq_expr_node(static_cast<OGRGeometry *>(nullptr)),
44 1 : swq_expr_node(std::make_unique<OGRPoint>(1, 2).get()),
45 2 : swq_expr_node(std::make_unique<OGRPoint>(1, 3).get()),
46 21 : };
47 : {
48 2 : auto node = swq_expr_node(SWQ_NOT);
49 1 : node.PushSubExpression(new swq_expr_node(1));
50 1 : nodes.emplace_back(node);
51 : }
52 : {
53 2 : auto node = swq_expr_node(SWQ_NOT);
54 1 : node.PushSubExpression(new swq_expr_node(2));
55 1 : nodes.emplace_back(node);
56 : }
57 : {
58 2 : auto node = swq_expr_node();
59 1 : node.eNodeType = SNT_COLUMN;
60 1 : node.field_index = 0;
61 1 : node.table_index = 0;
62 1 : nodes.emplace_back(node);
63 : }
64 : {
65 2 : auto node = swq_expr_node();
66 1 : node.eNodeType = SNT_COLUMN;
67 1 : node.field_index = 0;
68 1 : node.table_index = 0;
69 1 : node.table_name = CPLStrdup("foo");
70 1 : nodes.emplace_back(node);
71 : }
72 : {
73 2 : auto node = swq_expr_node();
74 1 : node.eNodeType = SNT_COLUMN;
75 1 : node.field_index = 0;
76 1 : node.table_index = 0;
77 1 : node.table_name = CPLStrdup("bar");
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 = 1;
84 1 : node.table_index = 0;
85 1 : nodes.emplace_back(node);
86 : }
87 : {
88 2 : auto node = swq_expr_node();
89 1 : node.eNodeType = SNT_COLUMN;
90 1 : node.field_index = 0;
91 1 : node.table_index = 1;
92 1 : nodes.emplace_back(node);
93 : }
94 :
95 23 : for (const auto &node1 : nodes)
96 : {
97 506 : for (const auto &node2 : nodes)
98 : {
99 484 : if (&node1 == &node2)
100 : {
101 22 : EXPECT_TRUE(node1 == node1);
102 22 : EXPECT_TRUE(node1 == swq_expr_node(node1));
103 : }
104 : else
105 : {
106 462 : EXPECT_FALSE(node1 == node2);
107 462 : EXPECT_FALSE(node2 == node1);
108 :
109 : {
110 924 : swq_expr_node copy(node1);
111 462 : copy = node2;
112 462 : EXPECT_TRUE(copy == node2);
113 : }
114 : {
115 924 : swq_expr_node copy1(node1);
116 924 : swq_expr_node copy2(node2);
117 462 : copy1 = std::move(copy2);
118 462 : EXPECT_TRUE(copy1 == node2);
119 : }
120 : }
121 : }
122 : }
123 1 : }
124 :
125 : class PushNotOperationDownToStackFixture
126 : : public test_ogr_swq,
127 : public ::testing::WithParamInterface<
128 : std::tuple<const char *, const char *>>
129 : {
130 : public:
131 1 : static std::vector<std::tuple<const char *, const char *>> GetTupleValues()
132 : {
133 : return {
134 : std::make_tuple("NOT(1 = 2)", "1 <> 2"),
135 : std::make_tuple("NOT(1 <> 2)", "1 = 2"),
136 : std::make_tuple("NOT(1 >= 2)", "1 < 2"),
137 : std::make_tuple("NOT(1 > 2)", "1 <= 2"),
138 : std::make_tuple("NOT(1 <= 2)", "1 > 2"),
139 : std::make_tuple("NOT(1 < 2)", "1 >= 2"),
140 : std::make_tuple("NOT(NOT(1))", "1"),
141 : std::make_tuple("NOT(1 AND 2)", "(NOT 1) OR (NOT 2)"),
142 : std::make_tuple("NOT(1 OR 2)", "(NOT 1) AND (NOT 2)"),
143 : std::make_tuple("3 AND NOT(1 OR 2)", "3 AND ((NOT 1) AND (NOT 2))"),
144 : std::make_tuple("NOT(NOT(1 = 2) OR 2)", "(1 = 2) AND (NOT 2)"),
145 : std::make_tuple("1", "1"),
146 1 : };
147 : }
148 : };
149 :
150 25 : TEST_P(PushNotOperationDownToStackFixture, test)
151 : {
152 12 : const char *pszInput = std::get<0>(GetParam());
153 12 : const char *pszExpected = std::get<1>(GetParam());
154 :
155 12 : swq_expr_node *poNode = nullptr;
156 12 : swq_expr_compile(pszInput, 0, nullptr, nullptr, true, nullptr, &poNode);
157 12 : ASSERT_TRUE(poNode);
158 12 : poNode->PushNotOperationDownToStack();
159 12 : char *pszStr = poNode->Unparse(nullptr, '"');
160 24 : std::string osStr = pszStr ? pszStr : "";
161 12 : CPLFree(pszStr);
162 12 : EXPECT_STREQ(osStr.c_str(), pszExpected);
163 12 : delete poNode;
164 : }
165 :
166 26 : INSTANTIATE_TEST_SUITE_P(
167 : test_ogr_swq, PushNotOperationDownToStackFixture,
168 : ::testing::ValuesIn(PushNotOperationDownToStackFixture::GetTupleValues()),
169 : [](const ::testing::TestParamInfo<
170 : PushNotOperationDownToStackFixture::ParamType> &l_info)
171 : {
172 : CPLString osStr = std::get<0>(l_info.param);
173 : osStr.replaceAll(' ', '_');
174 : osStr.replaceAll('(', '_');
175 : osStr.replaceAll(')', '_');
176 : osStr.replaceAll("<>", "NE");
177 : osStr.replaceAll(">=", "GE");
178 : osStr.replaceAll(">", "GT");
179 : osStr.replaceAll("<=", "LE");
180 : osStr.replaceAll("<", "LT");
181 : osStr.replaceAll('=', "EQ");
182 : osStr.replaceAll("__", '_');
183 : if (osStr.back() == '_')
184 : osStr.pop_back();
185 : return osStr;
186 : });
187 :
188 4 : TEST_F(test_ogr_swq, select_unparse)
189 : {
190 : {
191 2 : swq_select select;
192 1 : const char *pszSQL = "SELECT a FROM FOO";
193 1 : EXPECT_EQ(select.preparse(pszSQL), CE_None);
194 1 : char *ret = select.Unparse();
195 1 : EXPECT_STREQ(ret, pszSQL);
196 1 : CPLFree(ret);
197 : }
198 : {
199 2 : swq_select select;
200 1 : const char *pszSQL =
201 : "SELECT DISTINCT a, \"a b\" AS renamed, AVG(x.a) AS avg, MIN(a), "
202 : "MAX(\"a b\"), SUM(a), AVG(a), COUNT(a), COUNT(DISTINCT a), "
203 : "STDDEV_POP(a), STDDEV_SAMP(a) "
204 : "FROM 'foo'.\"FOO BAR\" AS x "
205 : "JOIN 'bar'.BAR AS y ON FOO.x = BAR.y "
206 : "WHERE 1 ORDER BY a, \"a b\" DESC "
207 : "LIMIT 1 OFFSET 2";
208 1 : EXPECT_EQ(select.preparse(pszSQL), CE_None);
209 1 : char *ret = select.Unparse();
210 1 : EXPECT_STREQ(ret, pszSQL);
211 1 : CPLFree(ret);
212 : }
213 1 : }
214 :
215 : } // namespace
|