Skip to content

Commit 0effab2

Browse files
committed
Add SqlConverter
Signed-off-by: Kunlin Yu <yukunlin@syriusrobotics.com>
1 parent 91632db commit 0effab2

File tree

9 files changed

+261
-56
lines changed

9 files changed

+261
-56
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,8 @@ build_dbg
3434
*.exe
3535
*.out
3636
*.app
37+
38+
# test file
39+
test/*.dot
40+
test/*.png
41+
test/test.sqlite

include/cql2cpp/ast_node.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,25 @@ class AstNode : public std::enable_shared_from_this<AstNode> {
4444
AstNode(NodeType type, Operator op, const std::vector<AstNodePtr> children)
4545
: type_(type), op_(op), children_(children) {
4646
id_ = idg.Gen();
47+
#ifdef DEBUG
4748
LOG(INFO) << "AstNode " << ToString() << std::endl;
49+
#endif
4850
}
4951

5052
AstNode(NodeType type, const ValueT& value)
5153
: type_(type), op_(NullOp), origin_value_(value), value_(NullValue) {
5254
id_ = idg.Gen();
55+
#ifdef DEBUG
5356
LOG(INFO) << "AstNode " << ToString() << std::endl;
57+
#endif
5458
}
5559

5660
AstNode(const ValueT& value)
5761
: type_(Literal), op_(NullOp), origin_value_(value), value_(NullValue) {
5862
id_ = idg.Gen();
63+
#ifdef DEBUG
5964
LOG(INFO) << "AstNode " << ToString() << std::endl;
65+
#endif
6066
}
6167

6268
const std::string& id() const { return id_; }

include/cql2cpp/cql2cpp.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "feature_source.h"
2323
#include "global_yylex.h"
2424
#include "tree_dot.h"
25+
#include "sql_converter.h"
2526

2627
#ifndef VERSION
2728
#define VERSION "0.0.0"
@@ -125,9 +126,8 @@ class Cql2Cpp {
125126
AstNodePtr root;
126127
if (not Parse(cql2_query, &root, error_msg)) return false;
127128

128-
*sql_where = cql2_query;
129-
130-
return true;
129+
SqlConverter converter;
130+
return converter.Convert(root, sql_where);
131131
}
132132

133133
static AstNodePtr ParseAsAst(const std::string& cql2_query, std::string* error_msg) {

include/cql2cpp/evaluator/bool.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* File Name: evaluator_bool.h
2+
* File Name: bool.h
33
*
44
* Copyright (c) 2024-2025 IndoorSpatial
55
*

include/cql2cpp/evaluator/compare.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* File Name: evaluator_compare.h
2+
* File Name: compare.h
33
*
44
* Copyright (c) 2024-2025 IndoorSpatial
55
*

include/cql2cpp/sql_converter.h

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/*
2+
* File Name: sql_converter.h
3+
*
4+
* Copyright (c) 2024-2025 IndoorSpatial
5+
*
6+
* Author: Kunlin Yu <yukunlin@syriusrobotics.com>
7+
* Create Date: 2025/05/12
8+
*
9+
*/
10+
11+
#pragma once
12+
#include <cql2cpp/ast_node.h>
13+
14+
#include <variant>
15+
16+
#include "geos/io/WKTWriter.h"
17+
18+
namespace cql2cpp {
19+
20+
using NodeConv = std::function<std::string(const AstNodePtr,
21+
const std::vector<std::string>&)>;
22+
23+
class SqlConverter {
24+
private:
25+
std::map<NodeType, std::map<Operator, NodeConv>> converters;
26+
mutable std::string error_msg_;
27+
28+
public:
29+
SqlConverter() {
30+
converters[BoolExpr][And] = [](auto n, auto c) -> std::string {
31+
return c.at(0) + " AND " + c.at(1);
32+
};
33+
converters[BoolExpr][Or] = [](auto n, auto c) -> std::string {
34+
return c.at(0) + " OR " + c.at(1);
35+
};
36+
converters[BoolExpr][Not] = [](auto n, auto c) -> std::string {
37+
return "NOT " + c.at(1);
38+
};
39+
converters[BinCompPred][Greater] = [](auto n, auto c) -> std::string {
40+
return c.at(0) + " > " + c.at(1);
41+
};
42+
converters[BinCompPred][GreaterEqual] = [](auto n, auto c) -> std::string {
43+
return c.at(0) + " >= " + c.at(1);
44+
};
45+
converters[BinCompPred][Lesser] = [](auto n, auto c) -> std::string {
46+
return c.at(0) + " < " + c.at(1);
47+
};
48+
converters[BinCompPred][LesserEqual] = [](auto n, auto c) -> std::string {
49+
return c.at(0) + " <= " + c.at(1);
50+
};
51+
converters[BinCompPred][NotEqual] = [](auto n, auto c) -> std::string {
52+
return c.at(0) + " <> " + c.at(1);
53+
};
54+
converters[BinCompPred][Equal] = [](auto n, auto c) -> std::string {
55+
return c.at(0) + " = " + c.at(1);
56+
};
57+
converters[Function][NullOp] = [](const AstNodePtr n, auto c) -> std::string {
58+
return std::get<std::string>(n->children().front()->origin_value()) + "(" + c.at(1) + ")";
59+
};
60+
converters[ArgumentList][NullOp] = [](auto n, auto c) -> std::string {
61+
std::stringstream ss;
62+
for (size_t i = 0; i < c.size(); i++) {
63+
if (i > 0) ss << ",";
64+
ss << c[i];
65+
}
66+
return ss.str();
67+
};
68+
converters[IsInListPred][In] = [](auto n, auto c) -> std::string {
69+
return c.at(0) + " IN (" + c.at(1) + ")";
70+
};
71+
converters[IsInListPred][NotIn] = [](auto n, auto c) -> std::string {
72+
return c.at(0) + " NOT IN (" + c.at(1) + ")";
73+
};
74+
converters[InList][NullOp] = [](auto n, auto c) -> std::string {
75+
std::stringstream ss;
76+
for (size_t i = 0; i < c.size(); i++) {
77+
if (i > 0) ss << ",";
78+
ss << c[i];
79+
}
80+
return ss.str();
81+
};
82+
converters[Literal][NullOp] = [](auto n, auto c) -> std::string {
83+
if (std::holds_alternative<bool>(n->origin_value()))
84+
return std::get<bool>(n->origin_value()) ? "TRUE" : "FALSE";
85+
else if (std::holds_alternative<int64_t>(n->origin_value()))
86+
return std::to_string(std::get<int64_t>(n->origin_value()));
87+
else if (std::holds_alternative<uint64_t>(n->origin_value()))
88+
return std::to_string(std::get<uint64_t>(n->origin_value()));
89+
else if (std::holds_alternative<double>(n->origin_value()))
90+
return std::to_string(std::get<double>(n->origin_value()));
91+
else if (std::holds_alternative<std::string>(n->origin_value()))
92+
return "'" + std::get<std::string>(n->origin_value()) + "'";
93+
else if (std::holds_alternative<const geos::geom::Geometry*>(
94+
n->origin_value())) {
95+
geos::io::WKTWriter writer;
96+
auto geom = std::get<const geos::geom::Geometry*>(n->origin_value());
97+
auto wkt = writer.write(geom);
98+
return "ST_GeomFromText('" + wkt + "')";
99+
} else if (std::holds_alternative<const geos::geom::Envelope*>(
100+
n->origin_value())) {
101+
const geos::geom::Envelope* env =
102+
std::get<const geos::geom::Envelope*>(n->origin_value());
103+
std::stringstream ss;
104+
ss << "ST_MakeEnvelope(";
105+
ss << env->getMinX() << "," << env->getMinY() << ",";
106+
ss << env->getMaxX() << "," << env->getMaxY() << ")";
107+
return ss.str();
108+
} else {
109+
// TODO: support array literal
110+
LOG(ERROR) << "unsupported array literal";
111+
return "";
112+
}
113+
};
114+
115+
converters[PropertyName][NullOp] = [](auto n, auto c) -> std::string {
116+
// TODO: this is database related. We should add an option for dialects
117+
// sqlite: "" / []
118+
// Oracle: ""
119+
// PostgreSQL: ""
120+
// MySQL: ``
121+
// SQL Server: []
122+
return "\"" + std::get<std::string>(n->origin_value()) + "\"";
123+
};
124+
converters[SpatialPred][S_Contains] = [](auto n, auto c) -> std::string {
125+
return "ST_Contains(" + c.at(0) + "," + c.at(1) + ")";
126+
};
127+
converters[SpatialPred][S_Crosses] = [](auto n, auto c) -> std::string {
128+
return "ST_Crosses(" + c.at(0) + "," + c.at(1) + ")";
129+
};
130+
converters[SpatialPred][S_Disjoint] = [](auto n, auto c) -> std::string {
131+
return "ST_Disjoint(" + c.at(0) + "," + c.at(1) + ")";
132+
};
133+
converters[SpatialPred][S_Equals] = [](auto n, auto c) -> std::string {
134+
return "ST_Equals(" + c.at(0) + "," + c.at(1) + ")";
135+
};
136+
converters[SpatialPred][S_Intersects] = [](auto n, auto c) -> std::string {
137+
return "ST_Intersects(" + c.at(0) + "," + c.at(1) + ")";
138+
};
139+
converters[SpatialPred][S_Overlaps] = [](auto n, auto c) -> std::string {
140+
return "ST_Overlaps(" + c.at(0) + "," + c.at(1) + ")";
141+
};
142+
converters[SpatialPred][S_Touches] = [](auto n, auto c) -> std::string {
143+
return "ST_Touches(" + c.at(0) + "," + c.at(1) + ")";
144+
};
145+
converters[SpatialPred][S_Within] = [](auto n, auto c) -> std::string {
146+
return "ST_Within(" + c.at(0) + "," + c.at(1) + ")";
147+
};
148+
}
149+
150+
void Register(
151+
const std::map<NodeType, std::map<Operator, NodeConv>> evaluators) {
152+
converters.insert(evaluators.begin(), evaluators.end());
153+
}
154+
155+
bool Convert(const AstNodePtr& root, std::string* sql_where) {
156+
if (converters.find(root->type()) == converters.end() ||
157+
converters.at(root->type()).find(root->op()) ==
158+
converters.at(root->type()).end()) {
159+
error_msg_ = "can not find sql converter for operator \"" +
160+
OpName.at(root->op()) + "\" in node type \"" +
161+
TypeName.at(root->type()) + "\"";
162+
return false;
163+
}
164+
165+
std::vector<std::string> children_sql;
166+
for (AstNodePtr child : root->children()) {
167+
std::string sub_sql;
168+
if (Convert(child, &sub_sql))
169+
children_sql.emplace_back(sub_sql);
170+
else
171+
return false;
172+
}
173+
174+
*sql_where = converters.at(root->type())
175+
.at(root->op())
176+
.
177+
operator()(root, children_sql);
178+
179+
return true;
180+
}
181+
};
182+
183+
} // namespace cql2cpp

src/cql2_lexer.l

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
using cql2cpp::Cql2ParserBase;
66

77
void print(const std::string& prefix, const char* yytext) {
8-
LOG(INFO) << "TOKEN " << prefix << " \"" << yytext << "\"";
8+
#ifdef DEBUG
9+
LOG(INFO) << "TOKEN " << prefix << " \"" << yytext << "\"";
10+
#endif
911
}
1012

1113
%}

src/cql2_parser.y

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,12 @@ using cql2cpp::NotIn;
3333
using cql2cpp::NameOp;
3434

3535
// print line number to debug
36-
#define PL LOG(INFO) << __LINE__
36+
#ifdef DEBUG
37+
#define PL LOG(INFO) << __LINE__
38+
#else
39+
#define PL
40+
#endif
41+
3742
#define MakeAstNode std::make_shared<AstNode>
3843

3944
void cql2cpp::Cql2ParserBase::error(const std::string& msg) {

0 commit comments

Comments
 (0)