TkN 2.1
Toolkit for Nuclei
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 * This software is governed by the CeCILL-B license under French law and *
11 * abiding by the rules of distribution of free software. You can use, *
12 * modify and/ or redistribute the software under the terms of the *
13 * CeCILL-B license as circulated by CEA, CNRS and INRIA at the following *
14 * URL \"http://www.cecill.info\". *
15 * *
16 * As a counterpart to the access to the source code and rights to copy, *
17 * modify and redistribute granted by the license, users are provided *
18 * only with a limited warranty and the software's author, the holder of *
19 * the economic rights, and the successive licensors have only limited *
20 * liability. *
21 * *
22 * In this respect, the user's attention is drawn to the risks associated *
23 * with loading, using, modifying and/or developing or reproducing the *
24 * software by the user in light of its specific status of free software, *
25 * that may mean that it is complicated to manipulate, and that also *
26 * therefore means that it is reserved for developers and experienced *
27 * professionals having in-depth computer knowledge. Users are therefore *
28 * encouraged to load and test the software's suitability as regards *
29 * their requirements in conditions enabling the security of their *
30 * systems and/or data to be ensured and, more generally, to use and *
31 * operate it in the same conditions as regards security. *
32 * *
33 * The fact that you are presently reading this means that you have had *
34 * knowledge of the CeCILL-B license and that you accept its terms. *
35 ********************************************************************************/
36
37#include "tkdatabase.h"
38
39#include <algorithm>
40
41#include "tksystem.h"
42
43namespace tkn {
53}
54
55using namespace tkn;
56
57tkdatabase *tkdatabase::g_database = nullptr;
58
59tkdatabase *tkdatabase::the_database()
60{
61 if(g_database) return g_database;
62
63 std::lock_guard<std::recursive_mutex> lock(get_mutex());
64
65 if ( g_database == nullptr ) {
66 g_database = new tkdatabase();
67 }
68
69 return g_database;
70}
71
72tkdatabase::tkdatabase(bool opening_db)
73{
74 std::lock_guard<std::recursive_mutex> lock(get_mutex());
75
76 if(g_database == nullptr && opening_db) open(tkstring::Form("%s/databases/TkN.db",TKN_SYS));
77 g_database = this;
78}
79
80sqlite3* tkdatabase::open(tkstring _db_name, tkstring _option)
81{
82 glog.set_class("database");
83 glog.set_method(tkstring::Form("open(%s,%s)",_db_name.data(),_option.data()));
84
85 if(TKN_SYS == nullptr) {
86 glog << error_v << " TKN_SYS environment variable not defined, cannot find the repository of the data bases !" << do_endl;
87 glog.clear();
88 return nullptr;
89 }
90
91 if(!_db_name.ends_with(".db")) _db_name.append(".db");
92
93 fName = _db_name;
94
95 _option.to_upper();
96
97 if(_option.equal_to("RECREATE")) {
98 if(gsystem->exists(fName)) {
99 glog << warning << "Existing database '" << _db_name << "' will be deleted and recreated." << do_endl;
100 gsystem->remove_file(fName,true);
101 }
102 fMode = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
103 }
104 else if(_option.equal_to("NEW")||_option.equal_to("CREATE")) fMode = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
105 else if(_option.equal_to("OPEN")) fMode = SQLITE_OPEN_READONLY;
106 else if(_option.equal_to("UPDATE")) fMode = SQLITE_OPEN_READWRITE;
107 else {
108 glog << warning_v << "'" << _option << "' option not suported." << do_endl;
109 glog << warning_v << "'OPEN' option used by default..." << do_endl;
110 fMode = SQLITE_OPEN_READONLY;
111 }
112
113 sqlite3* db;
114 if(sqlite3_open_v2(fName.data(), &db,fMode,nullptr) != SQLITE_OK) {
115 glog << error_v << "can't open database: '" << sqlite3_errmsg(db) << "'" << do_endl;
116 glog << info << "use tkn-db-update to download the database" << do_endl;
117 sqlite3_close(db);
118 glog.clear();
119 return nullptr;
120 }
121
122 fFullName = gsystem->get_system_command_output(tkstring::form("ls -l %s",fName.data()));
123 fFullName = fFullName.tokenize(" ").back();
124 fFullName.remove_all("\n");
125 fFullName = fFullName.tokenize("/").back();
126
127 fDataBase = db;
128 load_tables();
129 load_views();
130
131 exec_sql("PRAGMA synchronous = OFF");
132 exec_sql("PRAGMA temp_store = MEMORY");
133 exec_sql("PRAGMA mmap_size = 30000000000");
134 exec_sql("PRAGMA page_size = 32768 ");
135
136 glog.clear();
137 return db;
138}
139
141{
142 sqlite3_close(fDataBase);
143}
144
146{
147 const char *sql = "SELECT name FROM sqlite_master WHERE type='table'";
148 sqlite3_stmt* stmt;
149 sqlite3_prepare_v2(fDataBase, sql, -1, &stmt, nullptr);
150
151 while(sqlite3_step(stmt) == SQLITE_ROW) {
152 tkstring table_name(sqlite3_column_text(stmt, 0));
153 fTables.insert(std::make_pair(table_name,tkdb_table(table_name.data(),fDataBase)));
154 fTables[table_name.data()].load();
155 }
156 sqlite3_finalize(stmt);
157}
158
160{
161 const char *sql = "SELECT name FROM sqlite_master WHERE type='view'";
162 sqlite3_stmt* stmt;
163 sqlite3_prepare_v2(fDataBase, sql, -1, &stmt, nullptr);
164
165 while(sqlite3_step(stmt) == SQLITE_ROW) {
166 tkstring table_name(sqlite3_column_text(stmt, 0));
167 fViews.push_back(table_name);
168 }
169 sqlite3_finalize(stmt);
170}
171
173{
174 fTables.insert(std::make_pair(_table.get_name(),_table));
175}
176
177bool tkdatabase::has_table(const tkstring &_table_name)
178{
179 return fTables.count(_table_name)>0;
180}
181
182bool tkdatabase::has_view(const tkstring &_table_name)
183{
184 return std::count(fViews.begin(), fViews.end(), _table_name)>0;
185}
186
188{
189 fTables.insert(std::make_pair(_table_name,tkdb_table(_table_name.data(),fDataBase)));
190 return fTables[_table_name];
191}
192
194{
195 tkstring sql = tkstring::form("DROP TABLE %s ", _table_name.data());
196 exec_sql(sql.data());
197 fTables.erase(_table_name.data());
198}
199
201{
202 return &fTables[_table_name.data()];
203}
204
205void tkdatabase::print(const tkstring &_properties, const tkstring &_opt)
206{
207 for(auto &table : fTables){
208 for(auto &col : table.second.get_columns()) {
209 if(_properties.contains(col.first.data())||_properties.equal_to("*")) {
210 if(_opt.contains("notnull")&&!strcmp(col.second.get_value().data(),"")) continue;
211 glog << "|";
212 glog << col.first << "=" << col.second.get_value();
213 }
214 }
215 }
216 glog << do_endl;
217}
218
219void tkdatabase::begin(tkstring _selection, tkstring _from, tkstring _condition, tkstring _loop_name, tkstring _extra_cmd)
220{
221 glog.set_class("database");
222 glog.set_method(tkstring::Form("begin(%s,%s,%s,%s,%s)",_selection.data(),_from.data(),_condition.data(),_loop_name.data(),_extra_cmd.data()));
223
224 if(fSQLStatement.count(_loop_name)) {
225 glog << warning_v << "Previous sql statement not yet closed. Closing in to start new statement..." << do_endl;
226 end(_loop_name);
227 }
228
229 if(!_condition.empty()) _condition.prepend("where ");
230
231 sqlite3_stmt* sqlstm;
232 fSQLStatement.insert(std::make_pair(_loop_name,sqlstm));
233
234 int rc = sqlite3_prepare_v2(fDataBase, tkstring::form("select %s from %s %s",_selection.data(),_from.data(),_condition.data(),_extra_cmd.data()), -1, &fSQLStatement[_loop_name], nullptr);
235 if( rc!=SQLITE_OK ) {
236 glog << error_v << "SQL error: " << sqlite3_errmsg(fDataBase) << do_endl;
237 glog << tkstring::form("select %s from %s %s",_selection.data(),_from.data(),_condition.data()) << do_endl;
238 sqlite3_free(nullptr);
239 }
240 else fReading = true;
241
242 glog.clear();
243}
244
245bool tkdatabase::next(const tkstring &_loop_name)
246{
247 glog.set_class("database");
248 glog.set_method("next()");
249
250 if(!fSQLStatement.count(_loop_name)) {
251 glog << error_v << "No sql statement name "<< _loop_name << " prepared, please call begin() to prepare it " << do_endl;
252 glog.clear();
253 return false;
254 }
255
256 int ret_code = sqlite3_step(fSQLStatement[_loop_name]);
257 if(ret_code == SQLITE_ROW) {
258 int cols = sqlite3_column_count(fSQLStatement[_loop_name]);
259 for (int i=0; i<cols; i++) {
260 const unsigned char* tmp = sqlite3_column_text(fSQLStatement[_loop_name], i);
261 const char* col_name = sqlite3_column_name(fSQLStatement[_loop_name], i);
262 for(auto &table : fTables){
263 if(table.second.has_column(col_name)) {
264 if(tmp == nullptr)
265 table.second[col_name].set_value_str("");
266 else
267 table.second[col_name].set_value_str(reinterpret_cast<const char*>(tmp));
268 break;
269 }
270 }
271 }
272 glog.clear();
273 return true;
274 }
275 end(_loop_name);
276 glog.clear();
277 return false;
278}
279
280void tkdatabase::end(const tkstring &_loop_name)
281{
282 glog.set_class("database");
283 glog.set_method("end()");
284 if(!fSQLStatement.count(_loop_name)) {
285 glog << error_v << "No sql statement prepared to close..." << do_endl;
286 glog.clear();
287 return;
288 }
289 sqlite3_finalize(fSQLStatement[_loop_name]);
290 fReading = false;
291 fSQLStatement.erase(_loop_name);
292 for(auto &table : fTables) table.second.reset_column_values();
293 glog.clear();
294}
295
296int tkdatabase::count(const tkstring &_from, const tkstring &_condition)
297{
298 int counts = 0;
299 begin("*",_from,_condition,"count");
300 while(next("count")) counts++;
301 return counts;
302}
303
305{
306 glog.set_class("database");
307 glog.set_method(tkstring::Form("get_value(%s,%s,%s)",_selection.data(),_from.data(),_condition.data()));
308
309 if(_selection.contains(",")) glog << error_v << "Selection should contains only one column name !" << do_endl;
310 _condition += " LIMIT 1";
311
312 begin(_selection,_from,_condition,"get_value");
313 if(next("get_value")) {
314 tkstring val = "";
315 for(auto &table : fTables) if(table.second.has_column(_selection)) val = table.second[_selection.data()].get_value();
316 if(next("get_value")) glog << warning_v << "More than one entry corresponding to this criteria : only first value returned" << do_endl;
317 if(fSQLStatement.count("get_value")) end("get_value");
318 glog.clear();
319 return val;
320 }
321
322 glog << error_v << "No entry corresponding to the asked criteria" << do_endl;
323 glog.clear();
324 return "";
325}
326
327int tkdatabase::exec_sql(const char *_cmd)
328{
329 char *zErrMsg = nullptr;
330 int rc = sqlite3_exec(fDataBase, _cmd, nullptr, nullptr, &zErrMsg);
331
332 if( rc != SQLITE_OK ) {
333 glog << error << "SQL error: " << zErrMsg << " while excuting command: " << _cmd << do_endl;
334 sqlite3_free(zErrMsg);
335 }
336 else {}
337 return rc;
338}
339
340#ifdef HAS_ROOT
342ClassImp(tkdatabase);
343#endif
Interface to the sqlite database.
Definition: tkdatabase.h:57
int count(const tkstring &_from, const tkstring &_condition="")
resets column values and ends the select statement
Definition: tkdatabase.cpp:296
void end(const tkstring &_loop_name="default")
reads the next entry coresponding to the select statement and fills columns
Definition: tkdatabase.cpp:280
void remove_table(tkstring _table_name)
Definition: tkdatabase.cpp:193
void add_table(tkdb_table &_table)
Definition: tkdatabase.cpp:172
void begin(tkstring _selection, tkstring _from, tkstring _condition="", tkstring _loop_name="default", tkstring _extra_cmd="")
Definition: tkdatabase.cpp:219
tkstring get_value(tkstring _selection, tkstring _from, tkstring _condition="")
Definition: tkdatabase.cpp:304
tkdb_table & new_table(tkstring _table_name)
Definition: tkdatabase.cpp:187
sqlite3 * open(tkstring _db, tkstring _option="OPEN")
Definition: tkdatabase.cpp:80
bool has_view(const tkstring &_table_name)
Definition: tkdatabase.cpp:182
tkdatabase(bool opening_db=true)
Definition: tkdatabase.cpp:72
void print(const tkstring &_properties="", const tkstring &_opt="")
Definition: tkdatabase.cpp:205
int exec_sql(const char *_cmd)
returns the first value for selection
Definition: tkdatabase.cpp:327
bool has_table(const tkstring &_table_name)
Definition: tkdatabase.cpp:177
tkdb_table * get_table(tkstring _table_name)
Definition: tkdatabase.cpp:200
bool next(const tkstring &_loop_name="default")
executes the select statement
Definition: tkdatabase.cpp:245
Representaiton of a sqlite data table.
Definition: tkdb_table.h:52
tkstring get_name()
Definition: tkdb_table.h:116
std::string with usefull tricks from TString (ROOT) and KVString (KaliVeda) and more....
Definition: tkstring.h:54
static const char * form(const char *_format,...)
Definition: tkstring.cpp:431
std::vector< tkstring > tokenize(const tkstring &_delim=" ") const
Create a vector of string separated by at least one delimiter.
Definition: tkstring.cpp:254
static tkstring Form(const char *_format,...)
Definition: tkstring.cpp:345
tkstring & remove_all(const tkstring &_s1)
Definition: tkstring.h:215
bool ends_with(const char *_s, ECaseCompare _cmp=kExact) const
Definition: tkstring.cpp:241
bool equal_to(const char *_s, ECaseCompare _cmp=kExact) const
Returns true if the string and _s are identical.
Definition: tkstring.cpp:234
bool contains(const char *_pat, ECaseCompare _cmp=kExact) const
Definition: tkstring.h:197
tkstring & prepend(const tkstring &_st)
Definition: tkstring.h:224
tkstring & append(const tkstring &_st)
Definition: tkstring.h:227
tkstring & to_upper()
Change all letters to upper case.
Definition: tkstring.cpp:65
Definition: tklog.cpp:39
tklog & error_v(tklog &log)
Definition: tklog.h:430
tklog & info(tklog &log)
Definition: tklog.h:336
tklog & warning_v(tklog &log)
Definition: tklog.h:409
tklog & error(tklog &log)
Definition: tklog.h:367
tklog & do_endl(tklog &log)
Definition: tklog.h:235
tklog & warning(tklog &log)
Definition: tklog.h:354