vkdb
A time series database engine in C++.
Loading...
Searching...
No Matches
interpreter.cpp
1#include <vkdb/interpreter.h>
2#include <vkdb/database.h>
3
4namespace vkdb {
5RuntimeError::RuntimeError(Token token, const std::string& message) noexcept
6 : token_{token}, message_{message} {}
7
8Token RuntimeError::token() const noexcept {
9 return token_;
10}
11
12std::string RuntimeError::message() const noexcept {
13 return message_;
14}
15
16Interpreter::Interpreter(Database& database, error_callback callback) noexcept
17 : database_{database}, callback_{callback} {}
18
20 const Expr& expr,
21 std::ostream& stream
22) const noexcept {
23 try {
24 auto results{visit(expr)};
25 for (const auto& result : results) {
26 if (result.has_value()) {
27 stream << to_string(result.value()) << '\n';
28 }
29 }
30 } catch (const RuntimeError& error) {
31 callback_(error);
32 }
33}
34
35std::string Interpreter::to_string(const OutputResult& result) const {
36 return std::visit([this](auto&& result) -> std::string {
37 using R = std::decay_t<decltype(result)>;
38 if constexpr (std::is_same_v<R, SelectResult>) {
39 return to_string(result);
40 } else if constexpr (std::is_same_v<R, TablesResult>) {
41 return to_string(result);
42 }
43 }, result);
44}
45
46std::string Interpreter::to_string(const SelectResult& result) const {
47 return std::visit([this](auto&& result) -> std::string {
48 using R = std::decay_t<decltype(result)>;
49 if constexpr (std::is_same_v<R, SelectDataResult>) {
50 return datapointsToString<double>(result);
51 } else if constexpr (std::is_same_v<R, SelectDoubleResult>) {
52 return std::to_string(result);
53 } else if constexpr (std::is_same_v<R, SelectCountResult>) {
54 return std::to_string(result);
55 }
56 }, result);
57}
58
59std::string Interpreter::to_string(const TablesResult& result) const {
60 std::string tables_result{};
61 auto first{true};
62 for (const auto& table_name : result) {
63 if (!first) {
64 tables_result += " ";
65 }
66 tables_result += table_name;
67 first = false;
68 }
69 return tables_result;
70}
71
72Results Interpreter::visit(const Expr& expr) const {
73 try {
74 Results results{};
75 for (const auto& query : expr) {
76 results.push_back(visit(query));
77 }
78 return results;
79 } catch (const RuntimeError& e) {
80 throw e;
81 }
82}
83
84Result Interpreter::visit(const Query& query) const {
85 try {
86 return std::visit([this](auto&& query) -> Result {
87 using Q = std::decay_t<decltype(query)>;
88 if constexpr (std::is_same_v<Q, SelectQuery>) {
89 return visit(query);
90 } else if constexpr (std::is_same_v<Q, PutQuery>) {
91 visit(query);
92 return std::nullopt;
93 } else if constexpr (std::is_same_v<Q, DeleteQuery>) {
94 visit(query);
95 return std::nullopt;
96 } else if constexpr (std::is_same_v<Q, CreateQuery>) {
97 visit(query);
98 return std::nullopt;
99 } else if constexpr (std::is_same_v<Q, DropQuery>) {
100 visit(query);
101 return std::nullopt;
102 } else if constexpr (std::is_same_v<Q, AddQuery>) {
103 visit(query);
104 return std::nullopt;
105 } else if constexpr (std::is_same_v<Q, RemoveQuery>) {
106 visit(query);
107 return std::nullopt;
108 } else if constexpr (std::is_same_v<Q, TablesQuery>) {
109 return visit(query);
110 }
111 }, query);
112 } catch (const RuntimeError& e) {
113 throw e;
114 }
115}
116
117void Interpreter::add_optional_tag_list(
118 FriendlyQueryBuilder<double> &query_builder,
119 const std::optional<TagListExprResult>& tag_list
120) noexcept {
121 if (tag_list.has_value()) {
122 for (const auto& [key, value] : tag_list.value()) {
123 std::ignore = query_builder.whereTagsContain({key, value});
124 }
125 }
126}
127
128SelectResult Interpreter::handle_select_type(
129 FriendlyQueryBuilder<double> &query_builder,
130 SelectType type
131) {
132 return std::visit([&query_builder](auto&& type) -> SelectResult {
133 using T = std::decay_t<decltype(type)>;
134 try {
135 if constexpr (std::is_same_v<T, SelectTypeDataExpr>) {
136 return query_builder.execute();
137 } else if constexpr (std::is_same_v<T, SelectTypeCountExpr>) {
138 return query_builder.count();
139 } else if constexpr (std::is_same_v<T, SelectTypeAvgExpr>) {
140 return query_builder.avg();
141 } else if constexpr (std::is_same_v<T, SelectTypeSumExpr>) {
142 return query_builder.sum();
143 } else if constexpr (std::is_same_v<T, SelectTypeMinExpr>) {
144 return query_builder.min();
145 } else if constexpr (std::is_same_v<T, SelectTypeMaxExpr>) {
146 return query_builder.max();
147 }
148 } catch (const std::exception& e) {
149 throw RuntimeError{type.token, e.what()};
150 }
151 }, type);
152}
153
154SelectResult Interpreter::visit(const SelectQuery& query) const {
155 try {
156 auto type_result{visit(query.type)};
157 auto metric_result{visit(query.metric)};
158 auto table_name_result{visit(query.table_name)};
159 auto& table{database_.getTable(table_name_result)};
160 auto query_builder{table.query()
161 .whereMetricIs(metric_result)
162 };
163 std::visit([this, &query_builder](auto&& select_clause) -> void {
164 using C = std::decay_t<decltype(select_clause)>;
165 if constexpr (std::is_same_v<C, AllClause>) {
166 auto all_clause_result{visit(select_clause)};
167 add_optional_tag_list(query_builder, all_clause_result);
168 } else if constexpr (std::is_same_v<C, BetweenClause>) {
169 auto between_clause_result{visit(select_clause)};
170 std::ignore = query_builder.whereTimestampBetween(
171 std::get<0>(between_clause_result),
172 std::get<1>(between_clause_result)
173 );
174 add_optional_tag_list(
175 query_builder,
176 std::get<2>(between_clause_result)
177 );
178 } else if constexpr (std::is_same_v<C, AtClause>) {
179 auto at_clause_result{visit(select_clause)};
180 std::ignore = query_builder.whereTimestampIs(at_clause_result.first);
181 add_optional_tag_list(query_builder, at_clause_result.second);
182 }
183 }, query.clause);
184 return handle_select_type(query_builder, type_result);
185 } catch (const RuntimeError& e) {
186 throw e;
187 }
188}
189
190PutResult Interpreter::visit(const PutQuery& query) const {
191 try {
192 auto metric_result{visit(query.metric)};
193 auto timestamp_result{visit(query.timestamp)};
194 auto value_result{visit(query.value)};
195 auto table_name_result{visit(query.table_name)};
196
197 auto& table{database_.getTable(table_name_result)};
198 if (!query.tag_list.has_value()) {
199 table.query()
200 .put(timestamp_result, metric_result, {}, value_result)
201 .execute();
202 }
203
204 auto tag_list_result{visit(query.tag_list.value())};
205 table.query()
206 .put(timestamp_result, metric_result, tag_list_result, value_result)
207 .execute();
208 } catch (const std::exception& e) {
209 throw RuntimeError{query.metric.token, e.what()};
210 }
211}
212
213DeleteResult Interpreter::visit(const DeleteQuery& query) const {
214 try {
215 auto metric_result{visit(query.metric)};
216 auto timestamp_result{visit(query.timestamp)};
217 auto table_name_result{visit(query.table_name)};
218
219 auto& table{database_.getTable(table_name_result)};
220 if (!query.tag_list.has_value()) {
221 table.query()
222 .remove(timestamp_result, metric_result, {})
223 .execute();
224 }
225 auto tag_list_result{visit(query.tag_list.value())};
226 table.query()
227 .remove(timestamp_result, metric_result, tag_list_result)
228 .execute();
229 } catch (const std::exception& e) {
230 throw RuntimeError{query.metric.token, e.what()};
231 }
232}
233
234CreateResult Interpreter::visit(const CreateQuery& query) const {
235 try {
236 auto table_name_result{visit(query.table_name)};
237 database_.createTable(table_name_result);
238 if (query.tag_columns.has_value()) {
239 auto tag_columns_result{visit(query.tag_columns.value())};
240 database_
241 .getTable(table_name_result)
242 .setTagColumns(tag_columns_result);
243 }
244 } catch (const std::exception& e) {
245 throw RuntimeError{query.table_name.token, e.what()};
246 }
247}
248
249DropResult Interpreter::visit(const DropQuery& query) const {
250 try {
251 auto table_name_result{visit(query.table_name)};
252 database_.dropTable(table_name_result);
253 } catch (const std::exception& e) {
254 throw RuntimeError{query.table_name.token, e.what()};
255 }
256}
257
258AddResult Interpreter::visit(const AddQuery& query) const {
259 try {
260 auto tag_columns_result{visit(query.tag_columns)};
261 auto table_name_result{visit(query.table_name)};
262 auto& table{database_.getTable(table_name_result)};
263 for (const auto& tag_key : tag_columns_result) {
264 table.addTagColumn(tag_key);
265 }
266 } catch (const std::exception& e) {
267 throw RuntimeError{query.table_name.token, e.what()};
268 }
269}
270
271RemoveResult Interpreter::visit(const RemoveQuery& query) const {
272 try {
273 auto tag_columns_result{visit(query.tag_columns)};
274 auto table_name_result{visit(query.table_name)};
275 auto& table{database_.getTable(table_name_result)};
276 for (const auto& tag_key : tag_columns_result) {
277 table.removeTagColumn(tag_key);
278 }
279 } catch (const std::exception& e) {
280 throw RuntimeError{query.table_name.token, e.what()};
281 }
282}
283
284TablesResult Interpreter::visit(const TablesQuery& query) const {
285 try {
286 TablesResult tables_result{};
287 for (const auto& table_name : database_.tables()) {
288 tables_result.push_back(table_name);
289 }
290 return tables_result;
291 } catch (const std::exception& e) {
292 throw RuntimeError{query.token, e.what()};
293 }
294}
295
296AllClauseResult Interpreter::visit(const AllClause& clause) const {
297 AllClauseResult all_clause_result{};
298 if (clause.where_clause.has_value()) {
299 all_clause_result = visit(clause.where_clause.value());
300 }
301 return all_clause_result;
302}
303
304BetweenClauseResult Interpreter::visit(const BetweenClause& clause) const {
305 BetweenClauseResult between_clause_result{};
306 try {
307 std::get<0>(between_clause_result) = visit(clause.start);
308 std::get<1>(between_clause_result) = visit(clause.end);
309 if (clause.where_clause.has_value()) {
310 std::get<2>(between_clause_result) = visit(clause.where_clause.value());
311 }
312 return between_clause_result;
313 } catch (const std::exception& e) {
314 throw RuntimeError{clause.start.token, e.what()};
315 }
316}
317
318AtClauseResult Interpreter::visit(const AtClause& clause) const {
319 AtClauseResult at_clause_result{};
320 try {
321 at_clause_result.first = visit(clause.timestamp);
322 if (clause.where_clause.has_value()) {
323 at_clause_result.second = visit(clause.where_clause.value());
324 }
325 return at_clause_result;
326 } catch (const std::exception& e) {
327 throw RuntimeError{clause.timestamp.token, e.what()};
328 }
329}
330
331WhereClauseResult Interpreter::visit(const WhereClause& clause) const {
332 return visit(clause.tag_list);
333}
334
335SelectTypeResult Interpreter::visit(const SelectType& type) const noexcept {
336 return type;
337}
338
339MetricExprResult Interpreter::visit(const MetricExpr& metric) const noexcept {
340 return metric.token.lexeme();
341}
342
343TableNameExprResult Interpreter::visit(
344 const TableNameExpr& table_name
345) const noexcept {
346 return table_name.token.lexeme();
347}
348
349TagKeyExprResult Interpreter::visit(const TagKeyExpr& tag_key) const noexcept {
350 return tag_key.token.lexeme();
351}
352
353TagValueExprResult Interpreter::visit(
354 const TagValueExpr& tag_value
355) const noexcept {
356 return tag_value.token.lexeme();
357}
358
359TagExprResult Interpreter::visit(const TagExpr& tag) const noexcept {
360 auto tag_key_result{visit(tag.key)};
361 auto tag_value_result{visit(tag.value)};
362 return {tag_key_result, tag_value_result};
363}
364
365TagListExprResult Interpreter::visit(const TagListExpr& tag_list) const {
366 TagListExprResult tag_list_result;
367 for (const auto& tag : tag_list.tags) {
368 tag_list_result.emplace(visit(tag));
369 }
370 return tag_list_result;
371}
372
373TagColumnsExprResult Interpreter::visit(
374 const TagColumnsExpr& tag_columns
375) const {
376 TagColumnsExprResult tag_columns_result;
377 for (const auto& tag_key : tag_columns.keys) {
378 tag_columns_result.emplace(visit(tag_key));
379 }
380 return tag_columns_result;
381}
382
383TimestampExprResult Interpreter::visit(const TimestampExpr& timestamp) const {
384 try {
385 return std::stoull(timestamp.token.lexeme());
386 } catch (const std::exception& e) {
387 throw RuntimeError{timestamp.token, "Invalid timestamp."};
388 }
389}
390
391ValueExprResult Interpreter::visit(const ValueExpr& value) const {
392 try {
393 return std::stod(value.token.lexeme());
394 } catch (const std::exception& e) {
395 throw RuntimeError{value.token, "Invalid value."};
396 }
397}
398} // namespace vkdb
Represents a database in vkdb.
Definition database.h:20
Table & getTable(const TableName &table_name)
Get the Table object.
Definition database.cpp:32
std::vector< TableName > tables() const noexcept
Get the names of the tables in the database.
Definition database.cpp:63
void dropTable(const TableName &table_name)
Drop the Table object.
Definition database.cpp:41
Table & createTable(const TableName &table_name)
Create a Table object.
Definition database.cpp:21
void interpret(const Expr &expr, std::ostream &stream=std::cout) const noexcept
Interpret the expression.
Interpreter(Database &database, error_callback callback=[](const RuntimeError &) {}) noexcept
Construct a new Interpreter object.
Runtime error.
Token token() const noexcept
Get the token.
std::string message() const noexcept
Get the message.
RuntimeError(Token token, const std::string &message) noexcept
Construct a new Runtime Error object.
Table & setTagColumns(const TagColumns &tag_columns) noexcept
Set the TagColumns object.
Definition table.cpp:13
Represents a token.
Definition token.h:81