Delta Chat Core C-Library
mrsqlite3.c
1 /*******************************************************************************
2  *
3  * Delta Chat Core
4  * Copyright (C) 2017 Björn Petersen
5  * Contact: r10s@b44t.com, http://b44t.com
6  *
7  * This program is free software: you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later
10  * version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program. If not, see http://www.gnu.org/licenses/ .
19  *
20  ******************************************************************************/
21 
22 
23 #include "mrmailbox_internal.h"
24 
25 
26 /* This class wraps around SQLite. Some hints to the underlying database:
27 
28 - `PRAGMA cache_size` and `PRAGMA page_size`: As we save BLOBs in external
29  files, caching is not that important; we rely on the system defaults here
30  (normally 2 MB cache, 1 KB page size on sqlite < 3.12.0, 4 KB for newer
31  versions)
32 
33 - We use `sqlite3_last_insert_rowid()` to find out created records - for this
34  purpose, the primary ID has to be marked using `INTEGER PRIMARY KEY`, see
35  https://www.sqlite.org/c3ref/last_insert_rowid.html
36 
37 - Some words to the "param" fields: These fields contains a string with
38  additonal, named parameters which must not be accessed by a search and/or
39  are very seldomly used. Moreover, this allows smart minor database updates. */
40 
41 
42 /*******************************************************************************
43  * Tools
44  ******************************************************************************/
45 
46 
47 void mrsqlite3_log_error(mrsqlite3_t* ths, const char* msg_format, ...)
48 {
49  char* msg;
50  const char* notSetUp = "SQLite object not set up.";
51  va_list va;
52 
53  va_start(va, msg_format);
54  msg = sqlite3_vmprintf(msg_format, va); if( msg == NULL ) { mrmailbox_log_error(ths->m_mailbox, 0, "Bad log format string \"%s\".", msg_format); }
55  mrmailbox_log_error(ths->m_mailbox, 0, "%s SQLite says: %s", msg, ths->m_cobj? sqlite3_errmsg(ths->m_cobj) : notSetUp);
56  sqlite3_free(msg);
57  va_end(va);
58 }
59 
60 
61 sqlite3_stmt* mrsqlite3_prepare_v2_(mrsqlite3_t* ths, const char* querystr)
62 {
63  sqlite3_stmt* retStmt = NULL;
64 
65  if( ths == NULL || querystr == NULL || ths->m_cobj == NULL ) {
66  return NULL;
67  }
68 
69  if( sqlite3_prepare_v2(ths->m_cobj,
70  querystr, -1 /*read `sql` up to the first null-byte*/,
71  &retStmt,
72  NULL /*tail not interesing, we use only single statements*/) != SQLITE_OK )
73  {
74  mrsqlite3_log_error(ths, "Query failed: %s", querystr);
75  return NULL;
76  }
77 
78  /* success - the result mus be freed using sqlite3_finalize() */
79  return retStmt;
80 }
81 
82 
83 int mrsqlite3_execute__(mrsqlite3_t* ths, const char* querystr)
84 {
85  int success = 0;
86  sqlite3_stmt* stmt = NULL;
87  int sqlState;
88 
89  stmt = mrsqlite3_prepare_v2_(ths, querystr);
90  if( stmt == NULL ) {
91  goto cleanup;
92  }
93 
94  sqlState = sqlite3_step(stmt);
95  if( sqlState != SQLITE_DONE && sqlState != SQLITE_ROW ) {
96  mrsqlite3_log_error(ths, "Cannot excecute \"%s\".", querystr);
97  goto cleanup;
98  }
99 
100  success = 1;
101 
102 cleanup:
103  if( stmt ) {
104  sqlite3_finalize(stmt);
105  }
106  return success;
107 }
108 
109 
110 /*******************************************************************************
111  * Main interface
112  ******************************************************************************/
113 
114 
115 mrsqlite3_t* mrsqlite3_new(mrmailbox_t* mailbox)
116 {
117  mrsqlite3_t* ths = NULL;
118  int i;
119 
120  if( (ths=calloc(1, sizeof(mrsqlite3_t)))==NULL ) {
121  exit(24); /* cannot allocate little memory, unrecoverable error */
122  }
123 
124  ths->m_mailbox = mailbox;
125 
126  for( i = 0; i < PREDEFINED_CNT; i++ ) {
127  ths->m_pd[i] = NULL;
128  }
129 
130  pthread_mutex_init(&ths->m_critical_, NULL);
131 
132  return ths;
133 }
134 
135 
136 void mrsqlite3_unref(mrsqlite3_t* ths)
137 {
138  if( ths == NULL ) {
139  return;
140  }
141 
142  if( ths->m_cobj ) {
143  pthread_mutex_lock(&ths->m_critical_); /* as a very exeception, we do the locking inside the mrsqlite3-class - normally, this should be done by the caller! */
144  mrsqlite3_close__(ths);
145  pthread_mutex_unlock(&ths->m_critical_);
146  }
147 
148  pthread_mutex_destroy(&ths->m_critical_);
149  free(ths);
150 }
151 
152 
153 int mrsqlite3_open__(mrsqlite3_t* ths, const char* dbfile, int flags)
154 {
155  if( ths == NULL || dbfile == NULL ) {
156  goto cleanup;
157  }
158 
159  if( ths->m_cobj ) {
160  mrmailbox_log_error(ths->m_mailbox, 0, "Cannot open, database \"%s\" already opend.", dbfile);
161  goto cleanup;
162  }
163 
164  if( sqlite3_open(dbfile, &ths->m_cobj) != SQLITE_OK ) {
165  mrsqlite3_log_error(ths, "Cannot open database \"%s\".", dbfile); /* ususally, even for errors, the pointer is set up (if not, this is also checked by mrsqlite3_log_error()) */
166  goto cleanup;
167  }
168 
169  if( !(flags&MR_OPEN_READONLY) )
170  {
171  /* Init tables to dbversion=0 */
172  if( !mrsqlite3_table_exists__(ths, "config") )
173  {
174  mrmailbox_log_info(ths->m_mailbox, 0, "First time init: creating tables in \"%s\".", dbfile);
175 
176  mrsqlite3_execute__(ths, "CREATE TABLE config (id INTEGER PRIMARY KEY, keyname TEXT, value TEXT);");
177  mrsqlite3_execute__(ths, "CREATE INDEX config_index1 ON config (keyname);");
178 
179  mrsqlite3_execute__(ths, "CREATE TABLE contacts (id INTEGER PRIMARY KEY,"
180  " name TEXT DEFAULT '',"
181  " addr TEXT DEFAULT '' COLLATE NOCASE,"
182  " origin INTEGER DEFAULT 0,"
183  " blocked INTEGER DEFAULT 0,"
184  " last_seen INTEGER DEFAULT 0," /* last_seen is for future use */
185  " param TEXT DEFAULT '');"); /* param is for future use, eg. for the status */
186  mrsqlite3_execute__(ths, "CREATE INDEX contacts_index1 ON contacts (name COLLATE NOCASE);"); /* needed for query contacts */
187  mrsqlite3_execute__(ths, "CREATE INDEX contacts_index2 ON contacts (addr COLLATE NOCASE);"); /* needed for query and on receiving mails */
188  mrsqlite3_execute__(ths, "INSERT INTO contacts (id,name,origin) VALUES (1,'self',262144), (2,'system',262144), (3,'rsvd',262144), (4,'rsvd',262144), (5,'rsvd',262144), (6,'rsvd',262144), (7,'rsvd',262144), (8,'rsvd',262144), (9,'rsvd',262144);");
189  #if !defined(MR_ORIGIN_INTERNAL) || MR_ORIGIN_INTERNAL!=262144
190  #error
191  #endif
192 
193  mrsqlite3_execute__(ths, "CREATE TABLE chats (id INTEGER PRIMARY KEY, "
194  " type INTEGER DEFAULT 0,"
195  " name TEXT DEFAULT '',"
196  " draft_timestamp INTEGER DEFAULT 0,"
197  " draft_txt TEXT DEFAULT '',"
198  " blocked INTEGER DEFAULT 0,"
199  " grpid TEXT DEFAULT ''," /* contacts-global unique group-ID, see mrchat.c for details */
200  " param TEXT DEFAULT '');");
201  mrsqlite3_execute__(ths, "CREATE INDEX chats_index1 ON chats (grpid);");
202  mrsqlite3_execute__(ths, "CREATE TABLE chats_contacts (chat_id INTEGER, contact_id INTEGER);");
203  mrsqlite3_execute__(ths, "CREATE INDEX chats_contacts_index1 ON chats_contacts (chat_id);"); /* the other way round, an index on contact_id is only needed for blocking users */
204  #if !defined(MR_CHAT_TYPE_NORMAL) || MR_CHAT_TYPE_NORMAL!=100 || MR_CHAT_TYPE_GROUP!=120 || MR_CHAT_ID_DEADDROP!=1 || MR_CHAT_ID_TO_DEADDROP!=2 || MR_CHAT_ID_TRASH!=3 || MR_CHAT_ID_MSGS_IN_CREATION!=4 || MR_CHAT_ID_STARRED!=5 || MR_CHAT_ID_ARCHIVED_LINK!=6
205  mrsqlite3_execute__(ths, "INSERT INTO chats (id,type,name) VALUES (1,120,'deaddrop'), (2,120,'to_deaddrop'), (3,120,'trash'), (4,120,'msgs_in_creation'), (5,120,'starred'), (6,120,'archivedlink'), (7,100,'rsvd'), (8,100,'rsvd'), (9,100,'rsvd');");
206  #error
207  #endif
208 
209  mrsqlite3_execute__(ths, "CREATE TABLE msgs (id INTEGER PRIMARY KEY,"
210  " rfc724_mid TEXT DEFAULT ''," /* forever-global-unique Message-ID-string, unfortunately, this cannot be easily used to communicate via IMAP */
211  " server_folder TEXT DEFAULT ''," /* folder as used on the server, the folder will change when messages are moved around. */
212  " server_uid INTEGER DEFAULT 0," /* UID as used on the server, the UID will change when messages are moved around, unique together with validity, see RFC 3501; the validity may differ from folder to folder. We use the server_uid for "markseen" and to delete messages as we check against the message-id, we ignore the validity for these commands. */
213  " chat_id INTEGER DEFAULT 0,"
214  " from_id INTEGER DEFAULT 0,"
215  " to_id INTEGER DEFAULT 0," /* to_id is needed to allow moving messages eg. from "deaddrop" to a normal chat, may be unset */
216  " timestamp INTEGER DEFAULT 0,"
217  " type INTEGER DEFAULT 0,"
218  " state INTEGER DEFAULT 0,"
219  " msgrmsg INTEGER DEFAULT 1," /* does the message come from a messenger? (0=no, 1=yes, 2=no, but the message is a reply to a messenger message) */
220  " bytes INTEGER DEFAULT 0," /* not used, added in ~ v0.1.12 */
221  " txt TEXT DEFAULT ''," /* as this is also used for (fulltext) searching, nothing but normal, plain text should go here */
222  " txt_raw TEXT DEFAULT '',"
223  " param TEXT DEFAULT '');");
224  mrsqlite3_execute__(ths, "CREATE INDEX msgs_index1 ON msgs (rfc724_mid);"); /* in our database, one email may be split up to several messages (eg. one per image), so the email-Message-ID may be used for several records; id is always unique */
225  mrsqlite3_execute__(ths, "CREATE INDEX msgs_index2 ON msgs (chat_id);");
226  mrsqlite3_execute__(ths, "CREATE INDEX msgs_index3 ON msgs (timestamp);"); /* for sorting */
227  mrsqlite3_execute__(ths, "CREATE INDEX msgs_index4 ON msgs (state);"); /* for selecting the count of fresh messages (as there are normally only few unread messages, an index over the chat_id is not required for _this_ purpose */
228  mrsqlite3_execute__(ths, "INSERT INTO msgs (id,msgrmsg,txt) VALUES (1,0,'marker1'), (2,0,'rsvd'), (3,0,'rsvd'), (4,0,'rsvd'), (5,0,'rsvd'), (6,0,'rsvd'), (7,0,'rsvd'), (8,0,'rsvd'), (9,0,'daymarker');"); /* make sure, the reserved IDs are not used */
229 
230  mrsqlite3_execute__(ths, "CREATE TABLE jobs (id INTEGER PRIMARY KEY,"
231  " added_timestamp INTEGER,"
232  " desired_timestamp INTEGER DEFAULT 0,"
233  " action INTEGER,"
234  " foreign_id INTEGER,"
235  " param TEXT DEFAULT '');");
236  mrsqlite3_execute__(ths, "CREATE INDEX jobs_index1 ON jobs (desired_timestamp);");
237 
238  if( !mrsqlite3_table_exists__(ths, "config") || !mrsqlite3_table_exists__(ths, "contacts")
239  || !mrsqlite3_table_exists__(ths, "chats") || !mrsqlite3_table_exists__(ths, "chats_contacts")
240  || !mrsqlite3_table_exists__(ths, "msgs") || !mrsqlite3_table_exists__(ths, "jobs") )
241  {
242  mrsqlite3_log_error(ths, "Cannot create tables in new database \"%s\".", dbfile);
243  goto cleanup; /* cannot create the tables - maybe we cannot write? */
244  }
245 
246  mrsqlite3_set_config_int__(ths, "dbversion", 0);
247  }
248 
249  /* Update database */
250  int dbversion = mrsqlite3_get_config_int__(ths, "dbversion", 0);
251  #define NEW_DB_VERSION 1
252  if( dbversion < NEW_DB_VERSION )
253  {
254  mrsqlite3_execute__(ths, "CREATE TABLE leftgrps ("
255  " id INTEGER PRIMARY KEY,"
256  " grpid TEXT DEFAULT '');");
257  mrsqlite3_execute__(ths, "CREATE INDEX leftgrps_index1 ON leftgrps (grpid);");
258 
259  dbversion = NEW_DB_VERSION;
260  mrsqlite3_set_config_int__(ths, "dbversion", NEW_DB_VERSION);
261  }
262  #undef NEW_DB_VERSION
263 
264  #define NEW_DB_VERSION 2
265  if( dbversion < NEW_DB_VERSION )
266  {
267  mrsqlite3_execute__(ths, "ALTER TABLE contacts ADD COLUMN authname TEXT DEFAULT '';");
268 
269  dbversion = NEW_DB_VERSION;
270  mrsqlite3_set_config_int__(ths, "dbversion", NEW_DB_VERSION);
271  }
272  #undef NEW_DB_VERSION
273 
274  #define NEW_DB_VERSION 7
275  if( dbversion < NEW_DB_VERSION )
276  {
277  mrsqlite3_execute__(ths, "CREATE TABLE keypairs ("
278  " id INTEGER PRIMARY KEY,"
279  " addr TEXT DEFAULT '' COLLATE NOCASE,"
280  " is_default INTEGER DEFAULT 0,"
281  " private_key,"
282  " public_key,"
283  " created INTEGER DEFAULT 0);");
284 
285  dbversion = NEW_DB_VERSION;
286  mrsqlite3_set_config_int__(ths, "dbversion", NEW_DB_VERSION);
287  }
288  #undef NEW_DB_VERSION
289 
290  #define NEW_DB_VERSION 10
291  if( dbversion < NEW_DB_VERSION )
292  {
293  mrsqlite3_execute__(ths, "CREATE TABLE acpeerstates ("
294  " id INTEGER PRIMARY KEY,"
295  " addr TEXT DEFAULT '' COLLATE NOCASE," /* no UNIQUE here, Autocrypt: requires the index above mail+type (type however, is not used at the moment, but to be future-proof, we do not use an index. instead we just check ourself if there is a record or not)*/
296  " last_seen INTEGER DEFAULT 0,"
297  " last_seen_autocrypt INTEGER DEFAULT 0,"
298  " public_key,"
299  " prefer_encrypted INTEGER DEFAULT 0);");
300  mrsqlite3_execute__(ths, "CREATE INDEX acpeerstates_index1 ON acpeerstates (addr);");
301 
302  dbversion = NEW_DB_VERSION;
303  mrsqlite3_set_config_int__(ths, "dbversion", NEW_DB_VERSION);
304  }
305  #undef NEW_DB_VERSION
306 
307  #define NEW_DB_VERSION 12
308  if( dbversion < NEW_DB_VERSION )
309  {
310  mrsqlite3_execute__(ths, "CREATE TABLE msgs_mdns ("
311  " msg_id INTEGER, "
312  " contact_id INTEGER);");
313  mrsqlite3_execute__(ths, "CREATE INDEX msgs_mdns_index1 ON msgs_mdns (msg_id);");
314 
315  dbversion = NEW_DB_VERSION;
316  mrsqlite3_set_config_int__(ths, "dbversion", NEW_DB_VERSION);
317  }
318  #undef NEW_DB_VERSION
319 
320  #define NEW_DB_VERSION 17
321  if( dbversion < NEW_DB_VERSION )
322  {
323  mrsqlite3_execute__(ths, "ALTER TABLE chats ADD COLUMN archived INTEGER DEFAULT 0;");
324  mrsqlite3_execute__(ths, "CREATE INDEX chats_index2 ON chats (archived);");
325  mrsqlite3_execute__(ths, "ALTER TABLE msgs ADD COLUMN starred INTEGER DEFAULT 0;");
326  mrsqlite3_execute__(ths, "CREATE INDEX msgs_index5 ON msgs (starred);");
327 
328  dbversion = NEW_DB_VERSION;
329  mrsqlite3_set_config_int__(ths, "dbversion", NEW_DB_VERSION);
330  }
331  #undef NEW_DB_VERSION
332  }
333 
334  mrmailbox_log_info(ths->m_mailbox, 0, "Opened \"%s\" successfully.", dbfile);
335  return 1;
336 
337 cleanup:
338  mrsqlite3_close__(ths);
339  return 0;
340 }
341 
342 
343 void mrsqlite3_close__(mrsqlite3_t* ths)
344 {
345  int i;
346 
347  if( ths == NULL ) {
348  return;
349  }
350 
351  if( ths->m_cobj )
352  {
353  for( i = 0; i < PREDEFINED_CNT; i++ ) {
354  if( ths->m_pd[i] ) {
355  sqlite3_finalize(ths->m_pd[i]);
356  ths->m_pd[i] = NULL;
357  }
358  }
359 
360  sqlite3_close(ths->m_cobj);
361  ths->m_cobj = NULL;
362  }
363 
364  mrmailbox_log_info(ths->m_mailbox, 0, "Database closed."); /* We log the information even if not real closing took place; this is to detect logic errors. */
365 }
366 
367 
368 int mrsqlite3_is_open(const mrsqlite3_t* ths)
369 {
370  if( ths == NULL || ths->m_cobj == NULL ) {
371  return 0;
372  }
373  return 1;
374 }
375 
376 
377 sqlite3_stmt* mrsqlite3_predefine__(mrsqlite3_t* ths, size_t idx, const char* querystr)
378 {
379  /* predefines a statement or resets and reuses a statment.
380  Subsequent call may ommit the querystring.
381  CAVE: you must not call this function with different strings for the same index! */
382 
383  if( ths == NULL || ths->m_cobj == NULL || idx >= PREDEFINED_CNT ) {
384  return NULL;
385  }
386 
387  if( ths->m_pd[idx] ) {
388  sqlite3_reset(ths->m_pd[idx]);
389  return ths->m_pd[idx]; /* fine, already prepared before */
390  }
391 
392  /*prepare for the first time - this requires the querystring*/
393  if( querystr == NULL ) {
394  return NULL;
395  }
396 
397  if( sqlite3_prepare_v2(ths->m_cobj,
398  querystr, -1 /*read `sql` up to the first null-byte*/,
399  &ths->m_pd[idx],
400  NULL /*tail not interesing, we use only single statements*/) != SQLITE_OK )
401  {
402  mrsqlite3_log_error(ths, "Preparing statement \"%s\" failed.", querystr);
403  return NULL;
404  }
405 
406  return ths->m_pd[idx];
407 }
408 
409 
410 void mrsqlite3_reset_all_predefinitions(mrsqlite3_t* ths)
411 {
412  int i;
413  for( i = 0; i < PREDEFINED_CNT; i++ ) {
414  if( ths->m_pd[i] ) {
415  sqlite3_reset(ths->m_pd[i]);
416  }
417  }
418 }
419 
420 
421 int mrsqlite3_table_exists__(mrsqlite3_t* ths, const char* name)
422 {
423  int ret = 0;
424  char* querystr = NULL;
425  sqlite3_stmt* stmt = NULL;
426  int sqlState;
427 
428  if( (querystr=sqlite3_mprintf("PRAGMA table_info(%s)", name)) == NULL ) { /* this statement cannot be used with binded variables */
429  mrmailbox_log_error(ths->m_mailbox, 0, "mrsqlite3_table_exists_(): Out of memory.");
430  goto cleanup;
431  }
432 
433  if( (stmt=mrsqlite3_prepare_v2_(ths, querystr)) == NULL ) {
434  goto cleanup;
435  }
436 
437  sqlState = sqlite3_step(stmt);
438  if( sqlState == SQLITE_ROW ) {
439  ret = 1; /* the table exists. Other states are SQLITE_DONE or SQLITE_ERROR in both cases we return 0. */
440  }
441 
442  /* success - fall through to free allocated objects */
443  ;
444 
445  /* error/cleanup */
446 cleanup:
447  if( stmt ) {
448  sqlite3_finalize(stmt);
449  }
450 
451  if( querystr ) {
452  sqlite3_free(querystr);
453  }
454 
455  return ret;
456 }
457 
458 
459 /*******************************************************************************
460  * Handle configuration
461  ******************************************************************************/
462 
463 
464 int mrsqlite3_set_config__(mrsqlite3_t* ths, const char* key, const char* value)
465 {
466  int state;
467  sqlite3_stmt* stmt;
468 
469  if( key == NULL ) {
470  mrmailbox_log_error(ths->m_mailbox, 0, "mrsqlite3_set_config(): Bad parameter.");
471  return 0;
472  }
473 
474  if( !mrsqlite3_is_open(ths) ) {
475  mrmailbox_log_error(ths->m_mailbox, 0, "mrsqlite3_set_config(): Database not ready.");
476  return 0;
477  }
478 
479  if( value )
480  {
481  /* insert/update key=value */
482  #define SELECT_v_FROM_config_k_STATEMENT "SELECT value FROM config WHERE keyname=?;"
483  stmt = mrsqlite3_predefine__(ths, SELECT_v_FROM_config_k, SELECT_v_FROM_config_k_STATEMENT);
484  sqlite3_bind_text (stmt, 1, key, -1, SQLITE_STATIC);
485  state=sqlite3_step(stmt);
486  if( state == SQLITE_DONE ) {
487  stmt = mrsqlite3_predefine__(ths, INSERT_INTO_config_kv, "INSERT INTO config (keyname, value) VALUES (?, ?);");
488  sqlite3_bind_text (stmt, 1, key, -1, SQLITE_STATIC);
489  sqlite3_bind_text (stmt, 2, value, -1, SQLITE_STATIC);
490  state=sqlite3_step(stmt);
491 
492  }
493  else if( state == SQLITE_ROW ) {
494  stmt = mrsqlite3_predefine__(ths, UPDATE_config_vk, "UPDATE config SET value=? WHERE keyname=?;");
495  sqlite3_bind_text (stmt, 1, value, -1, SQLITE_STATIC);
496  sqlite3_bind_text (stmt, 2, key, -1, SQLITE_STATIC);
497  state=sqlite3_step(stmt);
498  }
499  else {
500  mrmailbox_log_error(ths->m_mailbox, 0, "mrsqlite3_set_config(): Cannot read value.");
501  return 0;
502  }
503  }
504  else
505  {
506  /* delete key */
507  stmt = mrsqlite3_predefine__(ths, DELETE_FROM_config_k, "DELETE FROM config WHERE keyname=?;");
508  sqlite3_bind_text (stmt, 1, key, -1, SQLITE_STATIC);
509  state=sqlite3_step(stmt);
510  }
511 
512  if( state != SQLITE_DONE ) {
513  mrmailbox_log_error(ths->m_mailbox, 0, "mrsqlite3_set_config(): Cannot change value.");
514  return 0;
515  }
516 
517  return 1;
518 }
519 
520 
521 char* mrsqlite3_get_config__(mrsqlite3_t* ths, const char* key, const char* def) /* the returned string must be free()'d, NULL is only returned if def is NULL */
522 {
523  sqlite3_stmt* stmt;
524 
525  if( !mrsqlite3_is_open(ths) || key == NULL ) {
526  return strdup_keep_null(def);
527  }
528 
529  stmt = mrsqlite3_predefine__(ths, SELECT_v_FROM_config_k, SELECT_v_FROM_config_k_STATEMENT);
530  sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
531  if( sqlite3_step(stmt) == SQLITE_ROW )
532  {
533  const unsigned char* ptr = sqlite3_column_text(stmt, 0); /* Do not pass the pointers returned from sqlite3_column_text(), etc. into sqlite3_free(). */
534  if( ptr )
535  {
536  /* success, fall through below to free objects */
537  return safe_strdup((const char*)ptr);
538  }
539  }
540 
541  /* return the default value */
542  return strdup_keep_null(def);
543 }
544 
545 
546 int32_t mrsqlite3_get_config_int__(mrsqlite3_t* ths, const char* key, int32_t def)
547 {
548  char* str = mrsqlite3_get_config__(ths, key, NULL);
549  if( str == NULL ) {
550  return def;
551  }
552  int32_t ret = atol(str);
553  free(str);
554  return ret;
555 }
556 
557 
558 int mrsqlite3_set_config_int__(mrsqlite3_t* ths, const char* key, int32_t value)
559 {
560  char* value_str = mr_mprintf("%i", (int)value);
561  if( value_str == NULL ) {
562  return 0;
563  }
564  int ret = mrsqlite3_set_config__(ths, key, value_str);
565  free(value_str);
566  return ret;
567 }
568 
569 
570 /*******************************************************************************
571  * Locking
572  ******************************************************************************/
573 
574 
575 void mrsqlite3_lock(mrsqlite3_t* ths) /* wait and lock */
576 {
577  pthread_mutex_lock(&ths->m_critical_);
578 
579  mrmailbox_wake_lock(ths->m_mailbox);
580 }
581 
582 
583 void mrsqlite3_unlock(mrsqlite3_t* ths)
584 {
585  mrmailbox_wake_unlock(ths->m_mailbox);
586 
587  pthread_mutex_unlock(&ths->m_critical_);
588 }
589 
590 
591 /*******************************************************************************
592  * Transactions
593  ******************************************************************************/
594 
595 
596 void mrsqlite3_begin_transaction__(mrsqlite3_t* ths)
597 {
598  sqlite3_stmt* stmt;
599 
600  ths->m_transactionCount++; /* this is safe, as the database should be locked when using a transaction */
601 
602  if( ths->m_transactionCount == 1 )
603  {
604  stmt = mrsqlite3_predefine__(ths, BEGIN_transaction, "BEGIN;");
605  if( sqlite3_step(stmt) != SQLITE_DONE ) {
606  mrsqlite3_log_error(ths, "Cannot begin transaction.");
607  }
608  }
609 }
610 
611 
612 void mrsqlite3_rollback__(mrsqlite3_t* ths)
613 {
614  sqlite3_stmt* stmt;
615 
616  if( ths->m_transactionCount >= 1 )
617  {
618  if( ths->m_transactionCount == 1 )
619  {
620  stmt = mrsqlite3_predefine__(ths, ROLLBACK_transaction, "ROLLBACK;");
621  if( sqlite3_step(stmt) != SQLITE_DONE ) {
622  mrsqlite3_log_error(ths, "Cannot rollback transaction.");
623  }
624  }
625 
626  ths->m_transactionCount--;
627  }
628 }
629 
630 
631 void mrsqlite3_commit__(mrsqlite3_t* ths)
632 {
633  sqlite3_stmt* stmt;
634 
635  if( ths->m_transactionCount >= 1 )
636  {
637  if( ths->m_transactionCount == 1 )
638  {
639  stmt = mrsqlite3_predefine__(ths, COMMIT_transaction, "COMMIT;");
640  if( sqlite3_step(stmt) != SQLITE_DONE ) {
641  mrsqlite3_log_error(ths, "Cannot commit transaction.");
642  }
643  }
644 
645  ths->m_transactionCount--;
646  }
647 }
An object representing a single mailbox.
Definition: mrmailbox.h:141