SOLA
Loading...
Searching...
No Matches
definitions.h
1// Copyright 2023 The SOLA authors
2//
3// This file is part of DAISI.
4//
5// DAISI is free software: you can redistribute it and/or modify it under the terms of the GNU
6// General Public License as published by the Free Software Foundation; version 2.
7//
8// DAISI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
9// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
10// Public License for more details.
11//
12// You should have received a copy of the GNU General Public License along with DAISI. If not, see
13// <https://www.gnu.org/licenses/>.
14//
15// SPDX-License-Identifier: GPL-2.0-only
16
17#ifndef DAISI_LOGGING_DEFINITIONS_H_
18#define DAISI_LOGGING_DEFINITIONS_H_
19
20#include <cstdarg>
21#include <cstdint>
22#include <functional>
23#include <map>
24#include <sstream>
25#include <string>
26#include <tuple>
27#include <type_traits>
28#include <unordered_set>
29
30// Args: (Application UUID)
31using LogDeviceApp = std::function<void(const std::string &)>;
32
33// Args: (SQL statement)
34using LogFunction = std::function<void(const std::string &)>;
35
36// Args: (Event UUID, Event type, Application ID)
37using LogEvent = std::function<void(const std::string &, uint8_t, const std::string &)>;
38
45template <typename... Tp> inline std::string toSQL(const std::string &format_str, Tp... values) {
46 // Calculate length of formatted statement
47 int len = snprintf(nullptr, 0, format_str.c_str(), values...);
48 if (len < 0) {
49 throw std::runtime_error("Unable to format SQL statement!");
50 }
51
52 // Write statement to string
53 std::string statement;
54 statement.resize(len);
55 int rc = snprintf(statement.data(), len + 1, format_str.c_str(), values...);
56 if (rc < 0) {
57 throw std::runtime_error("Unable to format SQL statement!");
58 }
59
60 return statement;
61}
62
64public:
65 std::string name;
66 std::string format = "NULL";
67 bool not_null = false;
68 bool is_id = false;
69 std::string foreign_key = "";
70 bool is_primary_key = false;
71 std::string data_type = "INTEGER";
72
73 void setDataType() {
74 static const std::unordered_set<std::string> int_specifiers{"%d", "%i", "%ld",
75 "%li", "%lu", "%u"};
76 static const std::unordered_set<std::string> real_specifiers{"%f", "%lf"};
77 bool plain_string = true;
78 std::string format_copy = format;
79 if (format.rfind("sql", 0) == 0) {
80 // If format starts with "sql", we will parse the value as an SQL statement
81 format_copy.erase(0, 3);
82 format = "%s";
83 plain_string = false;
84 }
85
86 if (int_specifiers.count(format_copy) != 0) {
87 data_type = "INTEGER";
88 } else if (real_specifiers.count(format_copy) != 0) {
89 data_type = "REAL";
90 } else if (format_copy == "%s") {
91 data_type = "TEXT";
92 if (plain_string) format = "'%s'";
93 } else if (format_copy == "NULL") {
94 data_type = "";
95 } else {
96 throw std::runtime_error("Unknown ColumnDataType");
97 }
98 }
99
100 // TODO: Declare as explicit(false) for implicit conversion in C++20
103 explicit DatabaseColumnInfo(std::string name) : name(std::move(name)), is_id(true){};
104
113 DatabaseColumnInfo(std::string name, std::string format, bool not_null = false,
114 std::string foreign_key = "", bool is_primary_key = false)
115 : name(std::move(name)),
116 format(std::move(format)),
117 not_null(not_null),
118 foreign_key(std::move(foreign_key)),
119 is_primary_key(is_primary_key) {
120 setDataType();
121 };
122};
123
125public:
126 std::string name;
127 std::vector<DatabaseColumnInfo> columns;
128 std::string additional_constraints;
129
135 explicit DatabaseTable(std::string name, std::vector<DatabaseColumnInfo> columns = {},
136 std::string additional_constraints = "")
137 : name(std::move(name)),
138 columns(std::move(columns)),
139 additional_constraints(std::move(additional_constraints)){};
140};
141
145inline std::string getCreateTableStatement(const DatabaseTable &table) {
146 std::ostringstream statement;
147 statement << "CREATE TABLE IF NOT EXISTS " << table.name << "(";
148
149 std::ostringstream foreign_keys;
150
151 // Iterate over columns and append each column name with its specification
152 // followed by a comma when it's not the last column
153 for (auto col_it = table.columns.begin(); col_it != table.columns.end(); col_it++) {
154 statement << col_it->name << " " << col_it->data_type;
155 if (col_it->is_id) {
156 statement << " NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE";
157 } else if (col_it->is_primary_key) {
158 statement << " NOT NULL PRIMARY KEY UNIQUE";
159 } else if (col_it->not_null) {
160 statement << " NOT NULL";
161 }
162 if (col_it->foreign_key.length() != 0U) {
163 // Needs to be appended after all columns are created
164 foreign_keys << ",FOREIGN KEY(" << col_it->name << ") REFERENCES " << col_it->foreign_key;
165 }
166
167 if (col_it != --table.columns.end()) {
168 statement << ",";
169 }
170 }
171
172 statement << foreign_keys.str();
173 if (!table.additional_constraints.empty()) {
174 statement << "," << table.additional_constraints;
175 }
176
177 statement << ");";
178 return statement.str();
179}
180
191inline std::string getCreateViewStatement(
192 const DatabaseTable &table, const std::unordered_map<std::string, std::string> &replacements,
193 const std::vector<std::string> &joins, const std::string &view_name = "") {
194 std::ostringstream statement;
195
196 std::string final_view_name = view_name.empty() ? "view" + table.name : view_name;
197 statement << "CREATE VIEW IF NOT EXISTS " << final_view_name << " AS SELECT ";
198
199 // Iterate over columns and append each column name with its specification
200 // followed by a comma when it's not the last column
201 for (auto col_it = table.columns.begin(); col_it != table.columns.end(); col_it++) {
202 auto repl = replacements.find(col_it->name);
203 if (repl != replacements.end()) {
204 if (!repl->second.empty()) {
205 // Append replaced column from joined table
206 statement << repl->second;
207 }
208 } else {
209 // Append own column
210 statement << table.name << "." << col_it->name;
211 }
212
213 // Append comma if another column follows after this one
214 if (col_it != --table.columns.end()) {
215 statement << ",";
216 }
217 }
218
219 statement << " FROM " << table.name;
220
221 // Append join definitions
222 if (!joins.empty()) {
223 statement << " ";
224 for (auto join_it = joins.begin(); join_it != joins.end(); join_it++) {
225 statement << *join_it;
226
227 // Append space if another join definition follows after this one
228 if (join_it != --joins.end()) {
229 statement << " ";
230 }
231 }
232 }
233
234 statement << ";";
235 return statement.str();
236}
237
244template <typename... Tp>
245std::string getInsertStatement(const DatabaseTable &table, const std::tuple<Tp...> &values) {
246 static constexpr bool kStringInTuple{(std::is_same_v<std::string, Tp> || ...)};
247 static_assert(!kStringInTuple,
248 "Error generating SQL Insert Statement: The tuple contains a string. "
249 "Convert the string to char array with .c_str() before calling!");
250
251 std::ostringstream statement;
252 statement << "INSERT INTO " << table.name << " VALUES(";
253
254 for (auto col_it = table.columns.begin(); col_it != table.columns.end(); col_it++) {
255 statement << col_it->format;
256
257 if (col_it != --table.columns.end()) {
258 statement << ",";
259 }
260 }
261
262 statement << ");";
263 const std::string format_str = statement.str();
264
265 // Calculate length of the formatted query
266 std::tuple<char *, int, const char *> buffer_format_tuple{nullptr, 0, format_str.c_str()};
267 auto snprintf_args = std::tuple_cat(buffer_format_tuple, values);
269 int len = std::apply(snprintf, snprintf_args);
270 if (len < 0) {
271 throw std::runtime_error("Unable to format SQL query!");
272 }
273
274 // Write query with now known length to string and return it
275 std::string query;
276 query.resize(len);
277 buffer_format_tuple = std::make_tuple(query.data(), len + 1, format_str.c_str());
278 snprintf_args = std::tuple_cat(buffer_format_tuple, values);
279 int rc = std::apply(snprintf, snprintf_args);
280 if (rc < 0) {
281 throw std::runtime_error("Unable to format SQL query!");
282 }
283 return query;
284}
285
286#endif
Definition definitions.h:63
DatabaseColumnInfo(std::string name)
Constructor for primary key id column.
Definition definitions.h:103
DatabaseColumnInfo(std::string name, std::string format, bool not_null=false, std::string foreign_key="", bool is_primary_key=false)
Constructor for regular and foreign key column.
Definition definitions.h:113
Definition definitions.h:124
DatabaseTable(std::string name, std::vector< DatabaseColumnInfo > columns={}, std::string additional_constraints="")
Constructor for a table definition.
Definition definitions.h:135