TkN 2.7.1
Toolkit for Nuclei
Loading...
Searching...
No Matches
tkdatabase.cpp
1/********************************************************************************
2 * Copyright (c) : Université de Lyon 1, CNRS/IN2P3, UMR5822, *
3 * IP2I, F-69622 Villeurbanne Cedex, France *
4 * Normandie Université, ENSICAEN, UNICAEN, CNRS/IN2P3, *
5 * LPC Caen, F-14000 Caen, France *
6 * Contibutor(s) : *
7 * Jérémie Dudouet jeremie.dudouet@cnrs.fr [2020] *
8 * Diego Gruyer diego.gruyer@cnrs.fr [2020] *
9 * *
10 * Licensed under the MIT License <http://opensource.org/licenses/MIT>. *
11 * SPDX-License-Identifier: MIT *
12 ********************************************************************************/
13
14#include "tkdatabase.h"
15
16#include <algorithm>
17#include <filesystem>
18
19#include "tksystem.h"
20#include "tksqlite_runtime.h"
21
22namespace tkn {
32}
33
34using namespace tkn;
35
37{
38 static tkdatabase *instance = new tkdatabase();
39 return instance;
40}
41
42tkdatabase::tkdatabase(bool opening_db)
43{
44 if(opening_db) open(tkstring::Form("%s/TkN.db", TKN_DB_DIR));
45}
46
48{
49 if (fDataBase) close();
50}
51
52sqlite3* tkdatabase::open(tkstring _db_name, tkstring _option)
53{
54 glog.set_class("database");
55 glog.set_method(tkstring::Form("open(%s,%s)",_db_name.data(),_option.data()));
56
57#if TKN_USE_SYSTEM_SQLITE
58 const int runtime_version = sqlite3_libversion_number();
59 const auto compatibility = detail::check_sqlite_runtime(
60 runtime_version, SQLITE_VERSION_NUMBER, sqlite3_threadsafe());
61 if (compatibility != detail::sqlite_compatibility::compatible) {
62 const char *reason = compatibility == detail::sqlite_compatibility::too_old ?
63 "SQLite " TKN_SQLITE_MIN_VERSION " or newer is required" :
65 "loaded SQLite is older than the compile-time headers" :
66 "loaded SQLite was built without mutex support";
67 glog << error << "System SQLite rejected: " << reason
68 << " (runtime=" << runtime_version << ", headers=" << SQLITE_VERSION_NUMBER << ")" << do_endl;
69 glog.clear();
70 return nullptr;
71 }
72#endif
73
74 if(!_db_name.ends_with(".db")) _db_name.append(".db");
75
76 fName = _db_name;
77
78 _option.to_upper();
79
80 if(_option.equal_to("RECREATE")) {
81 if(gsystem->exists(fName)) {
82 glog << warning << "Existing database '" << _db_name << "' will be deleted and recreated." << do_endl;
83 gsystem->remove_file(fName,true);
84 }
85 fMode = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
86 }
87 else if(_option.equal_to("NEW")||_option.equal_to("CREATE")) fMode = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
88 else if(_option.equal_to("OPEN")) fMode = SQLITE_OPEN_READONLY;
89 else if(_option.equal_to("UPDATE")) fMode = SQLITE_OPEN_READWRITE;
90 else {
91 glog << warning_v << "'" << _option << "' option not suported." << do_endl;
92 glog << warning_v << "'OPEN' option used by default..." << do_endl;
93 fMode = SQLITE_OPEN_READONLY;
94 }
95
96 sqlite3* db;
97 if(sqlite3_open_v2(fName.data(), &db,fMode,nullptr) != SQLITE_OK) {
98 glog << error_v << "can't open database: '" << sqlite3_errmsg(db) << "'" << do_endl;
99 glog << info << "use tkn-db-update to download the database" << do_endl;
100 sqlite3_close(db);
101 glog.clear();
102 return nullptr;
103 }
104
105 std::error_code path_error;
106 const auto resolved_path = std::filesystem::canonical(fName.data(), path_error);
107 const auto displayed_path = path_error ? std::filesystem::path(fName.data()) : resolved_path;
108 fFullName = displayed_path.filename().string();
109
110 fDataBase = db;
111 load_tables();
112 load_views();
113
114 exec_sql("PRAGMA synchronous = OFF");
115 exec_sql("PRAGMA temp_store = MEMORY");
116 exec_sql("PRAGMA mmap_size = 30000000000");
117 exec_sql("PRAGMA page_size = 32768 ");
118
119 glog.clear();
120 return db;
121}
122
124{
125 for (auto &statement : fSQLStatement) sqlite3_finalize(statement.second);
126 fSQLStatement.clear();
127 fSQLColumnBindings.clear();
128 fReading = false;
129 fTables.clear();
130 fViews.clear();
131 sqlite3_close(fDataBase);
132 fDataBase = nullptr;
133}
134
136{
137 const char *sql = "SELECT name FROM sqlite_master WHERE type='table'";
138 sqlite3_stmt* stmt;
139 sqlite3_prepare_v2(fDataBase, sql, -1, &stmt, nullptr);
140
141 while(sqlite3_step(stmt) == SQLITE_ROW) {
142 tkstring table_name(sqlite3_column_text(stmt, 0));
143 fTables.insert(std::make_pair(table_name,tkdb_table(table_name.data(),fDataBase)));
144 fTables[table_name.data()].load();
145 }
146 sqlite3_finalize(stmt);
147}
148
150{
151 const char *sql = "SELECT name FROM sqlite_master WHERE type='view'";
152 sqlite3_stmt* stmt;
153 sqlite3_prepare_v2(fDataBase, sql, -1, &stmt, nullptr);
154
155 while(sqlite3_step(stmt) == SQLITE_ROW) {
156 tkstring table_name(sqlite3_column_text(stmt, 0));
157 fViews.push_back(table_name);
158 }
159 sqlite3_finalize(stmt);
160}
161
163{
164 fTables.insert(std::make_pair(_table.get_name(),_table));
165}
166
167bool tkdatabase::has_table(const tkstring &_table_name)
168{
169 return fTables.count(_table_name)>0;
170}
171
172bool tkdatabase::has_view(const tkstring &_table_name)
173{
174 return std::count(fViews.begin(), fViews.end(), _table_name)>0;
175}
176
178{
179 fTables.insert(std::make_pair(_table_name,tkdb_table(_table_name.data(),fDataBase)));
180 return fTables[_table_name];
181}
182
184{
185 tkstring sql = tkstring::form("DROP TABLE %s ", _table_name.data());
186 exec_sql(sql.data());
187 fTables.erase(_table_name.data());
188}
189
191{
192 return &fTables[_table_name.data()];
193}
194
195void tkdatabase::print(const tkstring &_properties, const tkstring &_opt)
196{
197 for(auto &table : fTables){
198 for(auto &col : table.second.get_columns()) {
199 if(_properties.contains(col.first.data())||_properties.equal_to("*")) {
200 if(_opt.contains("notnull")&&!strcmp(col.second.get_value().data(),"")) continue;
201 glog << "|";
202 glog << col.first << "=" << col.second.get_value();
203 }
204 }
205 }
206 glog << do_endl;
207}
208
209void tkdatabase::begin(tkstring _selection, tkstring _from, tkstring _condition, tkstring _loop_name, tkstring _extra_cmd)
210{
211 glog.set_class("database");
212 glog.set_method(tkstring::Form("begin(%s,%s,%s,%s,%s)",_selection.data(),_from.data(),_condition.data(),_loop_name.data(),_extra_cmd.data()));
213
214 if(fSQLStatement.count(_loop_name)) {
215 glog << warning_v << "Previous sql statement not yet closed. Closing in to start new statement..." << do_endl;
216 end(_loop_name);
217 }
218
219 if(!_condition.empty()) _condition.prepend("where ");
220
221 sqlite3_stmt* sqlstm = nullptr;
222 int rc = sqlite3_prepare_v2(fDataBase, tkstring::form("select %s from %s %s",_selection.data(),_from.data(),_condition.data(),_extra_cmd.data()), -1, &sqlstm, nullptr);
223 if( rc!=SQLITE_OK ) {
224 glog << error_v << "SQL error: " << sqlite3_errmsg(fDataBase) << do_endl;
225 glog << tkstring::form("select %s from %s %s",_selection.data(),_from.data(),_condition.data()) << do_endl;
226 if(sqlstm) sqlite3_finalize(sqlstm);
227 sqlite3_free(nullptr);
228 }
229 else {
230 fSQLStatement.emplace(_loop_name, sqlstm);
231 auto &column_bindings = fSQLColumnBindings[_loop_name];
232 const int column_count = sqlite3_column_count(sqlstm);
233 column_bindings.resize(column_count);
234 for (int column_index = 0; column_index < column_count; ++column_index) {
235 const tkstring column_name = sqlite3_column_name(sqlstm, column_index);
236 for (auto &table : fTables) {
237 if (auto *column = table.second.get_column(column_name))
238 column_bindings[column_index].push_back(column);
239 }
240 }
241 fReading = true;
242 }
243
244 glog.clear();
245}
246
247bool tkdatabase::next(const tkstring &_loop_name)
248{
249 const auto statement_entry = fSQLStatement.find(_loop_name);
250 if(statement_entry == fSQLStatement.end()) {
251 glog.set_class("database");
252 glog.set_method("next()");
253 glog << error_v << "No sql statement name "<< _loop_name << " prepared, please call begin() to prepare it " << do_endl;
254 glog.clear();
255 return false;
256 }
257
258 sqlite3_stmt *statement = statement_entry->second;
259 int ret_code = sqlite3_step(statement);
260 if(ret_code == SQLITE_ROW) {
261 const auto &column_bindings = fSQLColumnBindings.at(_loop_name);
262 for (int column_index = 0; column_index < static_cast<int>(column_bindings.size()); ++column_index) {
263 const auto *value = sqlite3_column_text(statement, column_index);
264 const char *text = value ? reinterpret_cast<const char *>(value) : "";
265 for (auto *column : column_bindings[column_index])
266 column->set_value_str(text);
267 }
268 return true;
269 }
270 end(_loop_name);
271 return false;
272}
273
274void tkdatabase::end(const tkstring &_loop_name)
275{
276 glog.set_class("database");
277 glog.set_method("end()");
278 if(!fSQLStatement.count(_loop_name)) {
279 glog << error_v << "No sql statement prepared to close..." << do_endl;
280 glog.clear();
281 return;
282 }
283 sqlite3_finalize(fSQLStatement[_loop_name]);
284 fReading = false;
285 fSQLStatement.erase(_loop_name);
286 fSQLColumnBindings.erase(_loop_name);
287 for(auto &table : fTables) table.second.reset_column_values();
288 glog.clear();
289}
290
291int tkdatabase::count(const tkstring &_from, const tkstring &_condition)
292{
293 tkstring sql = tkstring::form("SELECT COUNT(*) FROM %s", _from.data());
294 if (!_condition.empty()) sql += tkstring::form(" WHERE %s", _condition.data());
295
296 sqlite3_stmt *statement = nullptr;
297 const int prepare_rc = sqlite3_prepare_v2(fDataBase, sql.data(), -1, &statement, nullptr);
298 if (prepare_rc != SQLITE_OK) {
299 glog << error << "SQL count error: " << sqlite3_errmsg(fDataBase) << do_endl;
300 if (statement) sqlite3_finalize(statement);
301 return 0;
302 }
303
304 int counts = 0;
305 if (sqlite3_step(statement) == SQLITE_ROW) counts = sqlite3_column_int(statement, 0);
306 sqlite3_finalize(statement);
307 return counts;
308}
309
311{
312 glog.set_class("database");
313 glog.set_method(tkstring::Form("get_value(%s,%s,%s)",_selection.data(),_from.data(),_condition.data()));
314
315 if(_selection.contains(",")) glog << error_v << "Selection should contains only one column name !" << do_endl;
316 _condition += " LIMIT 1";
317
318 begin(_selection,_from,_condition,"get_value");
319 if(next("get_value")) {
320 tkstring val = "";
321 for(auto &table : fTables) if(table.second.has_column(_selection)) val = table.second[_selection.data()].get_value();
322 if(next("get_value")) glog << warning_v << "More than one entry corresponding to this criteria : only first value returned" << do_endl;
323 if(fSQLStatement.count("get_value")) end("get_value");
324 glog.clear();
325 return val;
326 }
327
328 glog << error_v << "No entry corresponding to the asked criteria" << do_endl;
329 glog.clear();
330 return "";
331}
332
333int tkdatabase::exec_sql(const char *_cmd)
334{
335 char *zErrMsg = nullptr;
336 int rc = sqlite3_exec(fDataBase, _cmd, nullptr, nullptr, &zErrMsg);
337
338 if( rc != SQLITE_OK ) {
339 glog << error << "SQL error: " << zErrMsg << " while excuting command: " << _cmd << do_endl;
340 sqlite3_free(zErrMsg);
341 }
342 else {}
343 return rc;
344}
345
346#ifdef HAS_ROOT
348ClassImp(tkdatabase);
349#endif
Interface to the sqlite database.
Definition tkdatabase.h:39
int count(const tkstring &_from, const tkstring &_condition="")
resets column values and ends the select statement
virtual ~tkdatabase()
void end(const tkstring &_loop_name="default")
reads the next entry coresponding to the select statement and fills columns
void remove_table(tkstring _table_name)
void add_table(tkdb_table &_table)
void begin(tkstring _selection, tkstring _from, tkstring _condition="", tkstring _loop_name="default", tkstring _extra_cmd="")
tkstring get_value(tkstring _selection, tkstring _from, tkstring _condition="")
tkdb_table & new_table(tkstring _table_name)
sqlite3 * open(tkstring _db, tkstring _option="OPEN")
bool has_view(const tkstring &_table_name)
tkdatabase(bool opening_db=true)
void print(const tkstring &_properties="", const tkstring &_opt="")
int exec_sql(const char *_cmd)
returns the first value for selection
bool has_table(const tkstring &_table_name)
tkdb_table * get_table(tkstring _table_name)
bool next(const tkstring &_loop_name="default")
executes the select statement
static tkdatabase * the_database()
Representaiton of a sqlite data table.
Definition tkdb_table.h:31
tkstring get_name()
Definition tkdb_table.h:102
std::string with usefull tricks from TString (ROOT) and KVString (KaliVeda) and more....
Definition tkstring.h:33
static const char * form(const char *_format,...)
Definition tkstring.cpp:438
static tkstring Form(const char *_format,...)
Definition tkstring.cpp:368
bool ends_with(const char *_s, ECaseCompare _cmp=kExact) const
Definition tkstring.cpp:272
bool equal_to(const char *_s, ECaseCompare _cmp=kExact) const
Returns true if the string and _s are identical.
Definition tkstring.cpp:259
bool contains(const char *_pat, ECaseCompare _cmp=kExact) const
Definition tkstring.h:184
tkstring & prepend(const tkstring &_st)
Definition tkstring.h:217
tkstring & append(const tkstring &_st)
Definition tkstring.h:220
tkstring & to_upper()
Change all letters to upper case.
Definition tkstring.cpp:124
constexpr sqlite_compatibility check_sqlite_runtime(int runtime, int headers, int mutexes)
Definition tklog.cpp:16
tklog & error_v(tklog &log)
Definition tklog.h:407
tklog & info(tklog &log)
Definition tklog.h:313
tklog & warning_v(tklog &log)
Definition tklog.h:386
tklog & error(tklog &log)
Definition tklog.h:344
tklog & do_endl(tklog &log)
Definition tklog.h:212
tklog & warning(tklog &log)
Definition tklog.h:331