14#include "tkdatabase.h"
20#include "tksqlite_runtime.h"
49 if (fDataBase)
close();
54 glog.set_class(
"database");
55 glog.set_method(
tkstring::Form(
"open(%s,%s)",_db_name.data(),_option.data()));
57#if TKN_USE_SYSTEM_SQLITE
58 const int runtime_version = sqlite3_libversion_number();
60 runtime_version, SQLITE_VERSION_NUMBER, sqlite3_threadsafe());
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;
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);
85 fMode = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
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;
93 fMode = SQLITE_OPEN_READONLY;
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;
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();
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 ");
125 for (
auto &statement : fSQLStatement) sqlite3_finalize(statement.second);
126 fSQLStatement.clear();
127 fSQLColumnBindings.clear();
131 sqlite3_close(fDataBase);
137 const char *sql =
"SELECT name FROM sqlite_master WHERE type='table'";
139 sqlite3_prepare_v2(fDataBase, sql, -1, &stmt,
nullptr);
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();
146 sqlite3_finalize(stmt);
151 const char *sql =
"SELECT name FROM sqlite_master WHERE type='view'";
153 sqlite3_prepare_v2(fDataBase, sql, -1, &stmt,
nullptr);
155 while(sqlite3_step(stmt) == SQLITE_ROW) {
156 tkstring table_name(sqlite3_column_text(stmt, 0));
157 fViews.push_back(table_name);
159 sqlite3_finalize(stmt);
164 fTables.insert(std::make_pair(_table.
get_name(),_table));
169 return fTables.count(_table_name)>0;
174 return std::count(fViews.begin(), fViews.end(), _table_name)>0;
179 fTables.insert(std::make_pair(_table_name,
tkdb_table(_table_name.data(),fDataBase)));
180 return fTables[_table_name];
187 fTables.erase(_table_name.data());
192 return &fTables[_table_name.data()];
197 for(
auto &table : fTables){
198 for(
auto &col : table.second.get_columns()) {
200 if(_opt.
contains(
"notnull")&&!strcmp(col.second.get_value().data(),
""))
continue;
202 glog << col.first <<
"=" << col.second.get_value();
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()));
214 if(fSQLStatement.count(_loop_name)) {
215 glog <<
warning_v <<
"Previous sql statement not yet closed. Closing in to start new statement..." <<
do_endl;
219 if(!_condition.empty()) _condition.
prepend(
"where ");
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);
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);
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;
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);
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;
283 sqlite3_finalize(fSQLStatement[_loop_name]);
285 fSQLStatement.erase(_loop_name);
286 fSQLColumnBindings.erase(_loop_name);
287 for(
auto &table : fTables) table.second.reset_column_values();
294 if (!_condition.empty()) sql +=
tkstring::form(
" WHERE %s", _condition.data());
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);
305 if (sqlite3_step(statement) == SQLITE_ROW) counts = sqlite3_column_int(statement, 0);
306 sqlite3_finalize(statement);
312 glog.set_class(
"database");
313 glog.set_method(
tkstring::Form(
"get_value(%s,%s,%s)",_selection.data(),_from.data(),_condition.data()));
315 if(_selection.
contains(
",")) glog <<
error_v <<
"Selection should contains only one column name !" <<
do_endl;
316 _condition +=
" LIMIT 1";
318 begin(_selection,_from,_condition,
"get_value");
319 if(
next(
"get_value")) {
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");
328 glog <<
error_v <<
"No entry corresponding to the asked criteria" <<
do_endl;
335 char *zErrMsg =
nullptr;
336 int rc = sqlite3_exec(fDataBase, _cmd,
nullptr,
nullptr, &zErrMsg);
338 if( rc != SQLITE_OK ) {
339 glog <<
error <<
"SQL error: " << zErrMsg <<
" while excuting command: " << _cmd <<
do_endl;
340 sqlite3_free(zErrMsg);
Interface to the sqlite database.
int count(const tkstring &_from, const tkstring &_condition="")
resets column values and ends the select statement
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.
std::string with usefull tricks from TString (ROOT) and KVString (KaliVeda) and more....
static const char * form(const char *_format,...)
static tkstring Form(const char *_format,...)
bool ends_with(const char *_s, ECaseCompare _cmp=kExact) const
bool equal_to(const char *_s, ECaseCompare _cmp=kExact) const
Returns true if the string and _s are identical.
bool contains(const char *_pat, ECaseCompare _cmp=kExact) const
tkstring & prepend(const tkstring &_st)
tkstring & append(const tkstring &_st)
tkstring & to_upper()
Change all letters to upper case.
constexpr sqlite_compatibility check_sqlite_runtime(int runtime, int headers, int mutexes)
tklog & error_v(tklog &log)
tklog & warning_v(tklog &log)
tklog & error(tklog &log)
tklog & do_endl(tklog &log)
tklog & warning(tklog &log)