Xmipp  v3.23.11-Nereus
sql_utils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  *
3  * Authors: David Strelak (davidstrelak@gmail.com)
4  *
5  * Unidad de Bioinformatica of Centro Nacional de Biotecnologia , CSIC
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20  * 02111-1307 USA
21  *
22  * All comments concerning this program package may be sent to the
23  * e-mail address 'xmipp@cnb.csic.es'
24  ***************************************************************************/
25 
26 #include <iostream>
27 #include <sstream>
28 #include "sql_utils.h"
29 #include "../xmipp_error.h"
30 
31 bool sqlUtils::addColumns(const std::vector<MDLabel> &columns,
32  sqlite3 *db, const std::string &table) {
33  // it seems that with columns, one cannot use data binding
34  auto query = "ALTER TABLE " + table + " ADD COLUMN ";
35  // FIXME currently, whole db is in one huge transaction. Finish whatever might be pending,
36  // do our business in a clean transaction and start a new transaction after (not to break the original code)
37  commitTrans(db);
38  beginTrans(db);
39 
40  for (auto c : columns) {
41  auto stmt = query + MDL::label2SqlColumn(c) + ";";
42  sqlite3_exec(db, stmt.c_str(), nullptr, nullptr, nullptr);
43  }
44 
45  endTrans(db);
46  beginTrans(db);
47  return checkError(db);
48 }
49 
50 void sqlUtils::extractValue(sqlite3_stmt *stmt, const int position, MDObject &valueOut)
51 {
52  switch (valueOut.type)
53  {
54  case LABEL_BOOL: //bools are int in sqlite3
55  valueOut.data.boolValue = sqlite3_column_int(stmt, position) == 1;
56  break;
57  case LABEL_INT:
58  valueOut.data.intValue = sqlite3_column_int(stmt, position);
59  break;
60  case LABEL_SIZET:
61  valueOut.data.longintValue = sqlite3_column_int(stmt, position);
62  break;
63  case LABEL_DOUBLE:
64  valueOut.data.doubleValue = sqlite3_column_double(stmt, position);
65  break;
66  case LABEL_STRING:
67  {
68  std::stringstream ss;
69  ss << sqlite3_column_text(stmt, position);
70  valueOut.data.stringValue->assign(ss.str());
71 
72  break;
73  }
75  case LABEL_VECTOR_SIZET:
76  {
77  std::stringstream ss;
78  ss << sqlite3_column_text(stmt, position);
79  valueOut.fromStream(ss);
80  break;
81  }
82  default:
83  REPORT_ERROR(ERR_ARG_INCORRECT,"Do not know how to extract a value of type " + valueOut.type);
84  }
85 }
86 
87 bool sqlUtils::select(size_t rowId,
88  sqlite3 *db, const std::string &table,
89  std::vector<MDObject> &values) {
90  // assuming all records are the same
91  auto query = createSelectQuery(rowId, values, table);
92 
93  // FIXME currently, whole db is in one huge transaction. Finish whatever might be pending,
94  // do our business in a clean transaction and start a new transaction after (not to break the original code)
95  sqlite3_mutex_enter(sqlite3_db_mutex(db)); // FIXME this should be only done on demand or not at all ...
96  commitTrans(db);
97  beginTrans(db);
98 
99  sqlite3_stmt *stmt = nullptr;
100  sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr);
101 
102  // execute
103  sqlite3_step(stmt);
104  for (size_t i = 0; i < values.size(); ++i) {
105  extractValue(stmt, i, values.at(i));
106  }
107 
108  sqlite3_reset(stmt);
109 
110  sqlite3_finalize(stmt);
111  endTrans(db);
112  beginTrans(db);
113  sqlite3_mutex_leave(sqlite3_db_mutex(db)); // FIXME this should be only done on demand or not at all ...
114 
115  return checkError(db);
116 }
117 
119  sqlite3 *db, const std::string &table,
120  const std::vector<MDObject> &columns,
121  std::vector<std::vector<MDObject>> &rows) {
122  // assuming all records are the same
123  auto query = createSelectQuery(columns, table);
124  sqlite3_stmt *stmt = nullptr;
125  // FIXME currently, whole db is in one huge transaction. Finish whatever might be pending,
126  // do our business in a clean transaction and start a new transaction after (not to break the original code)
127  commitTrans(db);
128  beginTrans(db);
129  sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr);
130 
131  // execute, extract value from each row
132  while (sqlite3_step(stmt) == SQLITE_ROW) {
133  rows.emplace_back(columns);
134  auto &r = rows.back();
135  for (size_t i = 0; i < columns.size(); ++i) {
136  extractValue(stmt, i, r.at(i));
137  }
138  }
139  sqlite3_reset(stmt);
140 
141  sqlite3_finalize(stmt);
142  endTrans(db);
143  beginTrans(db);
144 
145  return checkError(db);
146 }
147 
148 
149 std::string sqlUtils::createSelectQuery(size_t id,
150  const std::vector<MDObject> &values,
151  const std::string &table) {
152  std::stringstream cols;
153  const auto len = values.size();
154  for (size_t i = 0; i < len; ++i) {
155  cols << MDL::label2StrSql(values.at(i).label);
156  if (len != (i + 1)) {
157  cols << ", ";
158  }
159  }
160  std::stringstream ss;
161  ss << "SELECT "
162  << cols.str()
163  << " FROM " << table
164  << " WHERE objID=" << id << ";";
165  return ss.str();
166 }
167 
169  const std::vector<MDObject> &values,
170  const std::string &table) {
171  std::stringstream cols;
172  const auto len = values.size();
173  for (size_t i = 0; i < len; ++i) {
174  cols << MDL::label2StrSql(values.at(i).label);
175  if (len != (i + 1)) {
176  cols << ", ";
177  }
178  }
179  std::stringstream ss;
180  ss << "SELECT "
181  << cols.str()
182  << " FROM " << table << ";";
183  return ss.str();
184 }
185 
187  const std::vector<const MDObject*> &values,
188  const std::string &table,
189  size_t id) {
190  std::stringstream cols;
191  const auto len = values.size();
192  for (size_t i = 0; i < len; ++i) {
193  cols << MDL::label2StrSql(values.at(i)->label);
194  cols << "=?";
195  if (len != (i + 1)) {
196  cols << ", ";
197  }
198  }
199  std::stringstream ss;
200  ss << "UPDATE " << table << " SET "
201  << cols.str()
202  << " WHERE objID=" << id << ";";
203  return ss.str();
204 }
205 
206 bool sqlUtils::update(const std::vector<const MDObject*> &values,
207  sqlite3 *db, const std::string &table, size_t id) {
208  if (values.empty()) {
209  return true;
210  }
211  // assuming all records are the same
212  auto query = createUpdateQuery(values, table, id);
213  sqlite3_stmt *stmt = nullptr;
214  sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr);
215  // FIXME currently, whole db is in one huge transaction. Finish whatever might be pending,
216  // do our business in a clean transaction and start a new transaction after (not to break the original code)
217  commitTrans(db);
218  beginTrans(db);
219 
220  // bind proper values
221  for (auto i = 0; i < values.size(); ++i) {
222  bindValue(stmt, i + 1, *values.at(i));
223  }
224  // execute
225  sqlite3_step(stmt);
226  sqlite3_clear_bindings(stmt);
227  sqlite3_reset(stmt);
228 
229  sqlite3_finalize(stmt);
230  endTrans(db);
231  beginTrans(db);
232 
233  return checkError(db);
234 }
235 
237  const std::vector<const MDObject*> &values,
238  const std::string &table) {
239  std::stringstream cols;
240  std::stringstream vals;
241  const auto len = values.size();
242  for (size_t i = 0; i < len; ++i) {
243  cols << MDL::label2StrSql(values.at(i)->label);
244  vals << "?";
245  if (len != (i + 1)) {
246  cols << ", ";
247  vals << ", ";
248  }
249  }
250  std::stringstream ss;
251  ss << "INSERT INTO " << table
252  << " (" << cols.str() << ")"
253  << " VALUES "
254  << " (" << vals.str() << ");";
255  return ss.str();
256 }
257 
258 bool sqlUtils::insert(const std::vector<std::vector<const MDObject*>> &records,
259  sqlite3 *db, const std::string &table) {
260  if (0 == records.size()) {
261  return true;
262  }
263  // assuming all records are the same
264  const auto &rec = records.at(0);
265  auto query = createInsertQuery(rec, table);
266  sqlite3_stmt *stmt = nullptr;
267  sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr);
268  // FIXME currently, whole db is in one huge transaction. Finish whatever might be pending,
269  // do our business in a clean transaction and start a new transaction after (not to break the original code)
270  commitTrans(db);
271  beginTrans(db);
272 
273  const auto len = rec.size();
274  for (const auto &r : records) {
275  // bind proper values
276  for (size_t i = 0; i < len; ++i) {
277  bindValue(stmt, i + 1, *r.at(i));
278  }
279  // execute
280  sqlite3_step(stmt);
281  sqlite3_clear_bindings(stmt);
282  sqlite3_reset(stmt);
283  }
284  sqlite3_finalize(stmt);
285  endTrans(db);
286  beginTrans(db);
287 
288  return checkError(db);
289 }
290 
291 bool sqlUtils::insert(const std::vector<const MDObject*> &values,
292  sqlite3 *db, const std::string &table) {
293  return insert(std::vector<std::vector<const MDObject*>>{values}, db, table);
294 }
295 
296 int sqlUtils::bindValue(sqlite3_stmt *stmt, const int position, const MDObject &valueIn)
297 {
298  //First reset the statement
299  //rc = sqlite3_reset(stmt);
300  //std::cerr << "rc after reset: " << rc <<std::endl;
301  if (valueIn.failed)
302  {
303  // If a value was wronly parsed, set NULL in their sqlite entry
304  std::cerr << "WARNING!!! valueIn.failed = True, binding NULL" << std::endl;
305 
306  return sqlite3_bind_null(stmt, position);
307  }
308  else
309  {
310  switch (valueIn.type)
311  {
312  case LABEL_BOOL: //bools are int in sqlite3
313  return sqlite3_bind_int(stmt, position, valueIn.data.boolValue ? 1 : 0);
314  case LABEL_INT:
315  return sqlite3_bind_int(stmt, position, valueIn.data.intValue);
316  case LABEL_SIZET:
317  return sqlite3_bind_int(stmt, position, valueIn.data.longintValue);
318  case LABEL_DOUBLE:
319  return sqlite3_bind_double(stmt, position, valueIn.data.doubleValue);
320  case LABEL_STRING:
321  return sqlite3_bind_text(stmt, position, valueIn.data.stringValue->c_str(), -1, SQLITE_TRANSIENT);
322  case LABEL_VECTOR_DOUBLE:
323  case LABEL_VECTOR_SIZET:
324  return sqlite3_bind_text(stmt, position, valueIn.toString(false, true).c_str(), -1, SQLITE_TRANSIENT);
325  default:
326  REPORT_ERROR(ERR_ARG_INCORRECT,"Do not know how to handle this type");
327  }
328  }
329 }
330 
331 bool sqlUtils::checkError(sqlite3 *db) {
332  auto err = sqlite3_errcode(db);
333  if (0 != err
334  && 100 != err && 101 != err) { // probably not an error in case of the sqlite3_step (fingers crossed)
335  auto msg = sqlite3_errmsg(db);
336  std::cerr << "SQLite3 error: " << err
337  << "\n"
338  << msg << std::endl;
339  return false;
340  }
341 
342  return true;
343  }
static void beginTrans(sqlite3 *db)
Definition: sql_utils.h:150
static bool insert(const std::vector< std::vector< const MDObject *>> &records, sqlite3 *db, const std::string &table)
Definition: sql_utils.cpp:258
#define REPORT_ERROR(nerr, ErrormMsg)
Definition: xmipp_error.h:211
doublereal * c
static void endTrans(sqlite3 *db)
Definition: sql_utils.h:154
static void commitTrans(sqlite3 *db)
Definition: sql_utils.h:158
static String label2StrSql(const MDLabel label)
MDLabelType type
#define i
bool fromStream(std::istream &is, bool fromString=false)
static bool update(const std::vector< const MDObject *> &values, sqlite3 *db, const std::string &table, size_t id)
Definition: sql_utils.cpp:206
size_t longintValue
Incorrect argument received.
Definition: xmipp_error.h:113
ObjectData data
static bool checkError(sqlite3 *db)
Definition: sql_utils.cpp:331
static std::string createUpdateQuery(const std::vector< const MDObject *> &values, const std::string &table, size_t id)
Definition: sql_utils.cpp:186
String toString(bool withFormat=false, bool isSql=false) const
#define len
static std::string createSelectQuery(const std::vector< MDObject > &values, const std::string &table)
Definition: sql_utils.cpp:168
static String label2SqlColumn(const MDLabel label)
double doubleValue
static bool select(size_t rowId, sqlite3 *db, const std::string &table, std::vector< MDObject > &values)
Definition: sql_utils.cpp:87
String * stringValue
static std::string createInsertQuery(const std::vector< const MDObject *> &values, const std::string &table)
Definition: sql_utils.cpp:236
static bool addColumns(const std::vector< MDLabel > &columns, sqlite3 *db, const std::string &table)
Definition: sql_utils.cpp:31