1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-imap-folder.c: class for an imap folder */
6 * Sankar P <psankar@novell.com>
7 * Srinivasa Ragavan <sragavan@novell.com>
9 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of version 2 of the GNU Lesser General Public
13 * License as published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
27 #include "camel-string-utils.h"
36 #include <glib/gi18n-lib.h>
38 #include "camel-debug.h"
39 #include "camel-object.h"
41 /* how long to wait before invoking sync on the file */
42 #define SYNC_TIMEOUT_SECONDS 5
44 #define READER_LOCK(cdb) g_rw_lock_reader_lock (&cdb->priv->rwlock)
45 #define READER_UNLOCK(cdb) g_rw_lock_reader_unlock (&cdb->priv->rwlock)
46 #define WRITER_LOCK(cdb) g_rw_lock_writer_lock (&cdb->priv->rwlock)
47 #define WRITER_UNLOCK(cdb) g_rw_lock_writer_unlock (&cdb->priv->rwlock)
49 static sqlite3_vfs *old_vfs = NULL;
50 static GThreadPool *sync_pool = NULL;
54 sqlite3_file *old_vfs_file; /* pointer to old_vfs' file */
61 call_old_file_Sync (CamelSqlite3File *cFile,
64 g_return_val_if_fail (old_vfs != NULL, SQLITE_ERROR);
65 g_return_val_if_fail (cFile != NULL, SQLITE_ERROR);
67 g_return_val_if_fail (cFile->old_vfs_file->pMethods != NULL, SQLITE_ERROR);
68 return cFile->old_vfs_file->pMethods->xSync (cFile->old_vfs_file, flags);
77 struct SyncRequestData
79 CamelSqlite3File *cFile;
81 SyncDone *done; /* not NULL when waiting for a finish; will be freed by the caller */
85 sync_request_thread_cb (gpointer task_data,
88 struct SyncRequestData *sync_data = task_data;
91 g_return_if_fail (sync_data != NULL);
92 g_return_if_fail (sync_data->cFile != NULL);
94 call_old_file_Sync (sync_data->cFile, sync_data->flags);
96 done = sync_data->done;
100 g_mutex_lock (&done->mutex);
102 g_cond_broadcast (&done->cond);
103 g_mutex_unlock (&done->mutex);
108 sync_push_request (CamelSqlite3File *cFile,
109 gboolean wait_for_finish)
111 struct SyncRequestData *data;
112 SyncDone *done = NULL;
113 GError *error = NULL;
115 g_return_if_fail (cFile != NULL);
116 g_return_if_fail (sync_pool != NULL);
118 g_rec_mutex_lock (&cFile->sync_mutex);
121 /* nothing to sync, might be when xClose is called
122 without any pending xSync request */
123 g_rec_mutex_unlock (&cFile->sync_mutex);
127 if (wait_for_finish) {
128 done = g_slice_new (SyncDone);
129 g_cond_init (&done->cond);
130 g_mutex_init (&done->mutex);
131 done->is_set = FALSE;
134 data = g_new0 (struct SyncRequestData, 1);
136 data->flags = cFile->flags;
141 g_rec_mutex_unlock (&cFile->sync_mutex);
143 g_thread_pool_push (sync_pool, data, &error);
146 g_warning ("%s: Failed to push to thread pool: %s\n", G_STRFUNC, error->message);
147 g_error_free (error);
150 g_cond_clear (&done->cond);
151 g_mutex_clear (&done->mutex);
152 g_slice_free (SyncDone, done);
159 g_mutex_lock (&done->mutex);
160 while (!done->is_set)
161 g_cond_wait (&done->cond, &done->mutex);
162 g_mutex_unlock (&done->mutex);
164 g_cond_clear (&done->cond);
165 g_mutex_clear (&done->mutex);
166 g_slice_free (SyncDone, done);
171 sync_push_request_timeout (CamelSqlite3File *cFile)
173 g_rec_mutex_lock (&cFile->sync_mutex);
175 if (cFile->timeout_id != 0) {
176 sync_push_request (cFile, FALSE);
177 cFile->timeout_id = 0;
180 g_rec_mutex_unlock (&cFile->sync_mutex);
185 #define def_subclassed(_nm, _params, _call) \
187 camel_sqlite3_file_ ## _nm _params \
189 CamelSqlite3File *cFile; \
191 g_return_val_if_fail (old_vfs != NULL, SQLITE_ERROR); \
192 g_return_val_if_fail (pFile != NULL, SQLITE_ERROR); \
194 cFile = (CamelSqlite3File *) pFile; \
195 g_return_val_if_fail (cFile->old_vfs_file->pMethods != NULL, SQLITE_ERROR); \
196 return cFile->old_vfs_file->pMethods->_nm _call; \
199 def_subclassed (xRead, (sqlite3_file *pFile, gpointer pBuf, gint iAmt, sqlite3_int64 iOfst), (cFile->old_vfs_file, pBuf, iAmt, iOfst))
200 def_subclassed (xWrite, (sqlite3_file *pFile, gconstpointer pBuf, gint iAmt, sqlite3_int64 iOfst), (cFile->old_vfs_file, pBuf, iAmt, iOfst))
201 def_subclassed (xTruncate, (sqlite3_file *pFile, sqlite3_int64 size), (cFile->old_vfs_file, size))
202 def_subclassed (xFileSize, (sqlite3_file *pFile, sqlite3_int64 *pSize), (cFile->old_vfs_file, pSize))
203 def_subclassed (xLock, (sqlite3_file *pFile, gint lockType), (cFile->old_vfs_file, lockType))
204 def_subclassed (xUnlock, (sqlite3_file *pFile, gint lockType), (cFile->old_vfs_file, lockType))
205 def_subclassed (xFileControl, (sqlite3_file *pFile, gint op, gpointer pArg), (cFile->old_vfs_file, op, pArg))
206 def_subclassed (xSectorSize, (sqlite3_file *pFile), (cFile->old_vfs_file))
207 def_subclassed (xDeviceCharacteristics, (sqlite3_file *pFile), (cFile->old_vfs_file))
209 #undef def_subclassed
212 camel_sqlite3_file_xCheckReservedLock (sqlite3_file *pFile,
215 CamelSqlite3File *cFile;
217 g_return_val_if_fail (old_vfs != NULL, SQLITE_ERROR);
218 g_return_val_if_fail (pFile != NULL, SQLITE_ERROR);
220 cFile = (CamelSqlite3File *) pFile;
221 g_return_val_if_fail (cFile->old_vfs_file->pMethods != NULL, SQLITE_ERROR);
223 /* check version in runtime */
224 if (sqlite3_libversion_number () < 3006000)
225 return ((gint (*)(sqlite3_file *)) (cFile->old_vfs_file->pMethods->xCheckReservedLock)) (cFile->old_vfs_file);
227 return ((gint (*)(sqlite3_file *, gint *)) (cFile->old_vfs_file->pMethods->xCheckReservedLock)) (cFile->old_vfs_file, pResOut);
231 camel_sqlite3_file_xClose (sqlite3_file *pFile)
233 CamelSqlite3File *cFile;
236 g_return_val_if_fail (old_vfs != NULL, SQLITE_ERROR);
237 g_return_val_if_fail (pFile != NULL, SQLITE_ERROR);
239 cFile = (CamelSqlite3File *) pFile;
241 g_rec_mutex_lock (&cFile->sync_mutex);
243 /* Cancel any pending sync requests. */
244 if (cFile->timeout_id > 0) {
245 g_source_remove (cFile->timeout_id);
246 cFile->timeout_id = 0;
249 g_rec_mutex_unlock (&cFile->sync_mutex);
251 /* Make the last sync. */
252 sync_push_request (cFile, TRUE);
254 if (cFile->old_vfs_file->pMethods)
255 res = cFile->old_vfs_file->pMethods->xClose (cFile->old_vfs_file);
259 g_free (cFile->old_vfs_file);
260 cFile->old_vfs_file = NULL;
262 g_rec_mutex_clear (&cFile->sync_mutex);
268 camel_sqlite3_file_xSync (sqlite3_file *pFile,
271 CamelSqlite3File *cFile;
273 g_return_val_if_fail (old_vfs != NULL, SQLITE_ERROR);
274 g_return_val_if_fail (pFile != NULL, SQLITE_ERROR);
276 cFile = (CamelSqlite3File *) pFile;
278 g_rec_mutex_lock (&cFile->sync_mutex);
280 /* If a sync request is already scheduled, accumulate flags. */
281 cFile->flags |= flags;
283 /* Cancel any pending sync requests. */
284 if (cFile->timeout_id > 0)
285 g_source_remove (cFile->timeout_id);
287 /* Wait SYNC_TIMEOUT_SECONDS before we actually sync. */
288 cFile->timeout_id = g_timeout_add_seconds (
289 SYNC_TIMEOUT_SECONDS, (GSourceFunc)
290 sync_push_request_timeout, cFile);
292 g_rec_mutex_unlock (&cFile->sync_mutex);
298 camel_sqlite3_vfs_xOpen (sqlite3_vfs *pVfs,
304 static GRecMutex only_once_lock;
305 static sqlite3_io_methods io_methods = {0};
306 CamelSqlite3File *cFile;
309 g_return_val_if_fail (old_vfs != NULL, -1);
310 g_return_val_if_fail (pFile != NULL, -1);
312 cFile = (CamelSqlite3File *) pFile;
313 cFile->old_vfs_file = g_malloc0 (old_vfs->szOsFile);
315 res = old_vfs->xOpen (old_vfs, zPath, cFile->old_vfs_file, flags, pOutFlags);
316 if (res != SQLITE_OK) {
317 g_free (cFile->old_vfs_file);
321 g_rec_mutex_init (&cFile->sync_mutex);
323 g_rec_mutex_lock (&only_once_lock);
326 sync_pool = g_thread_pool_new (sync_request_thread_cb, NULL, 2, FALSE, NULL);
328 /* cFile->old_vfs_file->pMethods is NULL when open failed for some reason,
329 * thus do not initialize our structure when do not know the version */
330 if (io_methods.xClose == NULL && cFile->old_vfs_file->pMethods) {
331 /* initialize our subclass function only once */
332 io_methods.iVersion = cFile->old_vfs_file->pMethods->iVersion;
334 /* check version in compile time */
335 #if SQLITE_VERSION_NUMBER < 3006000
336 io_methods.xCheckReservedLock = (gint (*)(sqlite3_file *)) camel_sqlite3_file_xCheckReservedLock;
338 io_methods.xCheckReservedLock = camel_sqlite3_file_xCheckReservedLock;
341 #define use_subclassed(x) io_methods.x = camel_sqlite3_file_ ## x
342 use_subclassed (xClose);
343 use_subclassed (xRead);
344 use_subclassed (xWrite);
345 use_subclassed (xTruncate);
346 use_subclassed (xSync);
347 use_subclassed (xFileSize);
348 use_subclassed (xLock);
349 use_subclassed (xUnlock);
350 use_subclassed (xFileControl);
351 use_subclassed (xSectorSize);
352 use_subclassed (xDeviceCharacteristics);
353 #undef use_subclassed
356 g_rec_mutex_unlock (&only_once_lock);
358 cFile->parent.pMethods = &io_methods;
364 init_sqlite_vfs (void)
366 static sqlite3_vfs vfs = { 0 };
368 old_vfs = sqlite3_vfs_find (NULL);
369 g_return_val_if_fail (old_vfs != NULL, NULL);
371 memcpy (&vfs, old_vfs, sizeof (sqlite3_vfs));
373 vfs.szOsFile = sizeof (CamelSqlite3File);
374 vfs.zName = "camel_sqlite3_vfs";
375 vfs.xOpen = camel_sqlite3_vfs_xOpen;
377 sqlite3_vfs_register (&vfs, 1);
382 #define d(x) if (camel_debug("sqlite")) x
383 #define START(stmt) if (camel_debug("dbtime")) { g_print ("\n===========\nDB SQL operation [%s] started\n", stmt); if (!cdb->priv->timer) { cdb->priv->timer = g_timer_new (); } else { g_timer_reset(cdb->priv->timer);} }
384 #define END if (camel_debug("dbtime")) { g_timer_stop (cdb->priv->timer); g_print ("DB Operation ended. Time Taken : %f\n###########\n", g_timer_elapsed (cdb->priv->timer, NULL)); }
385 #define STARTTS(stmt) if (camel_debug("dbtimets")) { g_print ("\n===========\nDB SQL operation [%s] started\n", stmt); if (!cdb->priv->timer) { cdb->priv->timer = g_timer_new (); } else { g_timer_reset(cdb->priv->timer);} }
386 #define ENDTS if (camel_debug("dbtimets")) { g_timer_stop (cdb->priv->timer); g_print ("DB Operation ended. Time Taken : %f\n###########\n", g_timer_elapsed (cdb->priv->timer, NULL)); }
388 struct _CamelDBPrivate {
392 gboolean transaction_is_on;
401 * Callers should hold the lock
404 cdb_sql_exec (sqlite3 *db,
406 gint (*callback)(gpointer ,gint,gchar **,gchar **),
410 gchar *errmsg = NULL;
413 d (g_print ("Camel SQL Exec:\n%s\n", stmt));
415 ret = sqlite3_exec (db, stmt, callback, data, &errmsg);
416 while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED || ret == -1) {
418 sqlite3_free (errmsg);
421 ret = sqlite3_exec (db, stmt, NULL, NULL, &errmsg);
424 if (ret != SQLITE_OK) {
425 d (g_print ("Error in SQL EXEC statement: %s [%s].\n", stmt, errmsg));
428 CAMEL_ERROR_GENERIC, "%s", errmsg);
429 sqlite3_free (errmsg);
435 sqlite3_free (errmsg);
442 /* checks whether string 'where' contains whole word 'what',
443 * case insensitively (ascii, not utf8, same as 'LIKE' in SQLite3)
446 cdb_match_func (sqlite3_context *ctx,
448 sqlite3_value **values)
450 gboolean matches = FALSE;
451 const gchar *what, *where;
453 g_return_if_fail (ctx != NULL);
454 g_return_if_fail (nArgs == 2);
455 g_return_if_fail (values != NULL);
457 what = (const gchar *) sqlite3_value_text (values[0]);
458 where = (const gchar *) sqlite3_value_text (values[1]);
460 if (what && where && !*what) {
462 } else if (what && where) {
463 gboolean word = TRUE;
466 for (i = 0, j = 0; where[i] && !matches; i++) {
472 } else if (word && tolower (c) == tolower (what[j])) {
474 if (what[j] == 0 && (where[i + 1] == 0 || isspace (where[i + 1])))
482 sqlite3_result_int (ctx, matches ? 1 : 0);
491 camel_db_open (const gchar *path,
494 static GOnce vfs_once = G_ONCE_INIT;
499 g_once (&vfs_once, (GThreadFunc) init_sqlite_vfs, NULL);
501 CAMEL_DB_USE_SHARED_CACHE;
503 ret = sqlite3_open (path, &db);
510 _("Insufficient memory"));
513 errmsg = sqlite3_errmsg (db);
514 d (g_print ("Can't open database %s: %s\n", path, errmsg));
517 CAMEL_ERROR_GENERIC, "%s", errmsg);
523 cdb = g_new (CamelDB, 1);
525 cdb->priv = g_new (CamelDBPrivate, 1);
526 cdb->priv->file_name = g_strdup (path);
527 g_rw_lock_init (&cdb->priv->rwlock);
528 cdb->priv->timer = NULL;
529 d (g_print ("\nDatabase succesfully opened \n"));
531 sqlite3_create_function (db, "MATCH", 2, SQLITE_UTF8, NULL, cdb_match_func, NULL, NULL);
533 /* Which is big / costlier ? A Stack frame or a pointer */
534 if (g_getenv ("CAMEL_SQLITE_DEFAULT_CACHE_SIZE") != NULL) {
537 cache = g_strdup_printf ("PRAGMA cache_size=%s", g_getenv ("CAMEL_SQLITE_DEFAULT_CACHE_SIZE"));
538 camel_db_command (cdb, cache, NULL);
542 camel_db_command (cdb, "ATTACH DATABASE ':memory:' AS mem", NULL);
544 if (g_getenv ("CAMEL_SQLITE_IN_MEMORY") != NULL) {
545 /* Optionally turn off Journaling, this gets over fsync issues, but could be risky */
546 camel_db_command (cdb, "PRAGMA main.journal_mode = off", NULL);
547 camel_db_command (cdb, "PRAGMA temp_store = memory", NULL);
550 sqlite3_busy_timeout (cdb->db, CAMEL_DB_SLEEP_INTERVAL);
561 camel_db_clone (CamelDB *cdb,
564 return camel_db_open (cdb->priv->file_name, error);
573 camel_db_close (CamelDB *cdb)
576 sqlite3_close (cdb->db);
577 g_rw_lock_clear (&cdb->priv->rwlock);
578 g_free (cdb->priv->file_name);
581 d (g_print ("\nDatabase succesfully closed \n"));
586 * camel_db_set_collate:
591 camel_db_set_collate (CamelDB *cdb,
593 const gchar *collate,
602 d (g_print ("Creating Collation %s on %s with %p\n", collate, col, (gpointer) func));
604 ret = sqlite3_create_collation (cdb->db, collate, SQLITE_UTF8, NULL, func);
616 camel_db_command (CamelDB *cdb,
628 ret = cdb_sql_exec (cdb->db, stmt, NULL, NULL, error);
637 * camel_db_begin_transaction:
642 camel_db_begin_transaction (CamelDB *cdb,
651 cdb->priv->transaction_is_on = TRUE;
653 return (cdb_sql_exec (cdb->db, "BEGIN", NULL, NULL, error));
657 * camel_db_end_transaction:
662 camel_db_end_transaction (CamelDB *cdb,
669 ret = cdb_sql_exec (cdb->db, "COMMIT", NULL, NULL, error);
670 cdb->priv->transaction_is_on = FALSE;
674 CAMEL_DB_RELEASE_SQLITE_MEMORY;
680 * camel_db_abort_transaction:
685 camel_db_abort_transaction (CamelDB *cdb,
690 ret = cdb_sql_exec (cdb->db, "ROLLBACK", NULL, NULL, error);
691 cdb->priv->transaction_is_on = FALSE;
694 CAMEL_DB_RELEASE_SQLITE_MEMORY;
700 * camel_db_add_to_transaction:
705 camel_db_add_to_transaction (CamelDB *cdb,
712 g_assert (cdb->priv->transaction_is_on == TRUE);
714 return (cdb_sql_exec (cdb->db, stmt, NULL, NULL, error));
718 * camel_db_transaction_command:
723 camel_db_transaction_command (CamelDB *cdb,
736 ret = cdb_sql_exec (cdb->db, "BEGIN", NULL, NULL, error);
741 query = qry_list->data;
742 ret = cdb_sql_exec (cdb->db, query, NULL, NULL, error);
745 qry_list = g_list_next (qry_list);
748 ret = cdb_sql_exec (cdb->db, "COMMIT", NULL, NULL, error);
756 count_cb (gpointer data,
763 for (i = 0; i < argc; i++) {
764 if (strstr (azColName[i], "COUNT")) {
765 *(guint32 *)data = argv [i] ? strtoul (argv [i], NULL, 10) : 0;
773 * camel_db_count_message_info:
778 camel_db_count_message_info (CamelDB *cdb,
788 ret = cdb_sql_exec (cdb->db, query, count_cb, count, error);
793 CAMEL_DB_RELEASE_SQLITE_MEMORY;
799 * camel_db_count_junk_message_info:
804 camel_db_count_junk_message_info (CamelDB *cdb,
805 const gchar *table_name,
815 query = sqlite3_mprintf ("SELECT COUNT (*) FROM %Q WHERE junk = 1", table_name);
817 ret = camel_db_count_message_info (cdb, query, count, error);
818 sqlite3_free (query);
824 * camel_db_count_unread_message_info:
829 camel_db_count_unread_message_info (CamelDB *cdb,
830 const gchar *table_name,
840 query = sqlite3_mprintf ("SELECT COUNT (*) FROM %Q WHERE read = 0", table_name);
842 ret = camel_db_count_message_info (cdb, query, count, error);
843 sqlite3_free (query);
849 * camel_db_count_visible_unread_message_info:
854 camel_db_count_visible_unread_message_info (CamelDB *cdb,
855 const gchar *table_name,
865 query = sqlite3_mprintf ("SELECT COUNT (*) FROM %Q WHERE read = 0 AND junk = 0 AND deleted = 0", table_name);
867 ret = camel_db_count_message_info (cdb, query, count, error);
868 sqlite3_free (query);
874 * camel_db_count_visible_message_info:
879 camel_db_count_visible_message_info (CamelDB *cdb,
880 const gchar *table_name,
890 query = sqlite3_mprintf ("SELECT COUNT (*) FROM %Q WHERE junk = 0 AND deleted = 0", table_name);
892 ret = camel_db_count_message_info (cdb, query, count, error);
893 sqlite3_free (query);
899 * camel_db_count_junk_not-deleted_message_info:
904 camel_db_count_junk_not_deleted_message_info (CamelDB *cdb,
905 const gchar *table_name,
915 query = sqlite3_mprintf ("SELECT COUNT (*) FROM %Q WHERE junk = 1 AND deleted = 0", table_name);
917 ret = camel_db_count_message_info (cdb, query, count, error);
918 sqlite3_free (query);
924 * camel_db_count_deleted_message_info:
929 camel_db_count_deleted_message_info (CamelDB *cdb,
930 const gchar *table_name,
940 query = sqlite3_mprintf ("SELECT COUNT (*) FROM %Q WHERE deleted = 1", table_name);
942 ret = camel_db_count_message_info (cdb, query, count, error);
943 sqlite3_free (query);
949 * camel_db_count_total_message_info:
954 camel_db_count_total_message_info (CamelDB *cdb,
955 const gchar *table_name,
966 query = sqlite3_mprintf ("SELECT COUNT (*) FROM %Q where read=0 or read=1", table_name);
968 ret = camel_db_count_message_info (cdb, query, count, error);
969 sqlite3_free (query);
980 camel_db_select (CamelDB *cdb,
982 CamelDBSelectCB callback,
991 d (g_print ("\n%s:\n%s \n", G_STRFUNC, stmt));
995 ret = cdb_sql_exec (cdb->db, stmt, callback, data, error);
999 CAMEL_DB_RELEASE_SQLITE_MEMORY;
1005 read_uids_callback (gpointer ref_array,
1010 GPtrArray *array = ref_array;
1012 g_return_val_if_fail (ncol == 1, 0);
1015 g_ptr_array_add (array, (gchar *) (camel_pstring_strdup (cols[0])));
1021 read_uids_to_hash_callback (gpointer ref_hash,
1026 GHashTable *hash = ref_hash;
1028 g_return_val_if_fail (ncol == 2, 0);
1031 g_hash_table_insert (hash, (gchar *) camel_pstring_strdup (cols[0]), GUINT_TO_POINTER (cols[1] ? strtoul (cols[1], NULL, 10) : 0));
1037 * camel_db_get_folder_uids:
1039 * Fills hash with uid->GUINT_TO_POINTER (flag)
1044 camel_db_get_folder_uids (CamelDB *db,
1045 const gchar *folder_name,
1046 const gchar *sort_by,
1047 const gchar *collate,
1054 sel_query = sqlite3_mprintf ("SELECT uid,flags FROM %Q%s%s%s%s", folder_name, sort_by ? " order by " : "", sort_by ? sort_by: "", (sort_by && collate) ? " collate " : "", (sort_by && collate) ? collate : "");
1056 ret = camel_db_select (db, sel_query, read_uids_to_hash_callback, hash, error);
1057 sqlite3_free (sel_query);
1063 * camel_db_get_folder_junk_uids:
1068 camel_db_get_folder_junk_uids (CamelDB *db,
1074 GPtrArray *array = g_ptr_array_new ();
1076 sel_query = sqlite3_mprintf ("SELECT uid FROM %Q where junk=1", folder_name);
1078 ret = camel_db_select (db, sel_query, read_uids_callback, array, error);
1080 sqlite3_free (sel_query);
1082 if (!array->len || ret != 0) {
1083 g_ptr_array_free (array, TRUE);
1090 * camel_db_get_folder_deleted_uids:
1095 camel_db_get_folder_deleted_uids (CamelDB *db,
1096 const gchar *folder_name,
1101 GPtrArray *array = g_ptr_array_new ();
1103 sel_query = sqlite3_mprintf ("SELECT uid FROM %Q where deleted=1", folder_name);
1105 ret = camel_db_select (db, sel_query, read_uids_callback, array, error);
1106 sqlite3_free (sel_query);
1108 if (!array->len || ret != 0) {
1109 g_ptr_array_free (array, TRUE);
1116 struct ReadPreviewData
1118 GHashTable *columns_hash;
1123 read_preview_callback (gpointer ref,
1128 struct ReadPreviewData *rpd = ref;
1129 const gchar *uid = NULL;
1133 for (i = 0; i < ncol; ++i) {
1134 if (!name[i] || !cols[i])
1137 switch (camel_db_get_column_ident (&rpd->columns_hash, i, ncol, name)) {
1138 case CAMEL_DB_COLUMN_UID:
1139 uid = camel_pstring_strdup (cols[i]);
1141 case CAMEL_DB_COLUMN_PREVIEW:
1142 msg = g_strdup (cols[i]);
1145 g_warn_if_reached ();
1150 g_hash_table_insert (rpd->hash, (gchar *) uid, msg);
1156 * camel_db_get_folder_preview:
1161 camel_db_get_folder_preview (CamelDB *db,
1162 const gchar *folder_name,
1167 struct ReadPreviewData rpd;
1168 GHashTable *hash = g_hash_table_new (g_str_hash, g_str_equal);
1170 sel_query = sqlite3_mprintf ("SELECT uid, preview FROM '%q_preview'", folder_name);
1172 rpd.columns_hash = NULL;
1175 ret = camel_db_select (db, sel_query, read_preview_callback, &rpd, error);
1176 sqlite3_free (sel_query);
1178 if (rpd.columns_hash)
1179 g_hash_table_destroy (rpd.columns_hash);
1181 if (!g_hash_table_size (hash) || ret != 0) {
1182 g_hash_table_destroy (hash);
1190 * camel_db_write_preview_record:
1195 camel_db_write_preview_record (CamelDB *db,
1196 const gchar *folder_name,
1204 query = sqlite3_mprintf ("INSERT OR REPLACE INTO '%q_preview' VALUES(%Q,%Q)", folder_name, uid, msg);
1206 ret = camel_db_add_to_transaction (db, query, error);
1207 sqlite3_free (query);
1213 * camel_db_create_folders_table:
1218 camel_db_create_folders_table (CamelDB *cdb,
1221 const gchar *query = "CREATE TABLE IF NOT EXISTS folders ( folder_name TEXT PRIMARY KEY, version REAL, flags INTEGER, nextuid INTEGER, time NUMERIC, saved_count INTEGER, unread_count INTEGER, deleted_count INTEGER, junk_count INTEGER, visible_count INTEGER, jnd_count INTEGER, bdata TEXT )";
1222 CAMEL_DB_RELEASE_SQLITE_MEMORY;
1223 return ((camel_db_command (cdb, query, error)));
1227 camel_db_create_message_info_table (CamelDB *cdb,
1228 const gchar *folder_name,
1232 gchar *table_creation_query, *safe_index;
1234 /* README: It is possible to compress all system flags into a single column and use just as userflags but that makes querying for other applications difficult an d bloats the parsing code. Instead, it is better to bloat the tables. Sqlite should have some optimizations for sparse columns etc. */
1235 table_creation_query = sqlite3_mprintf ("CREATE TABLE IF NOT EXISTS %Q ( uid TEXT PRIMARY KEY , flags INTEGER , msg_type INTEGER , read INTEGER , deleted INTEGER , replied INTEGER , important INTEGER , junk INTEGER , attachment INTEGER , dirty INTEGER , size INTEGER , dsent NUMERIC , dreceived NUMERIC , subject TEXT , mail_from TEXT , mail_to TEXT , mail_cc TEXT , mlist TEXT , followup_flag TEXT , followup_completed_on TEXT , followup_due_by TEXT , part TEXT , labels TEXT , usertags TEXT , cinfo TEXT , bdata TEXT, created TEXT, modified TEXT)", folder_name);
1236 ret = camel_db_add_to_transaction (cdb, table_creation_query, error);
1237 sqlite3_free (table_creation_query);
1239 table_creation_query = sqlite3_mprintf ("CREATE TABLE IF NOT EXISTS '%q_bodystructure' ( uid TEXT PRIMARY KEY , bodystructure TEXT )", folder_name);
1240 ret = camel_db_add_to_transaction (cdb, table_creation_query, error);
1241 sqlite3_free (table_creation_query);
1243 /* Create message preview table. */
1244 table_creation_query = sqlite3_mprintf ("CREATE TABLE IF NOT EXISTS '%q_preview' ( uid TEXT PRIMARY KEY , preview TEXT)", folder_name);
1245 ret = camel_db_add_to_transaction (cdb, table_creation_query, error);
1246 sqlite3_free (table_creation_query);
1248 /* FIXME: sqlize folder_name before you create the index */
1249 safe_index = g_strdup_printf ("SINDEX-%s", folder_name);
1250 table_creation_query = sqlite3_mprintf ("DROP INDEX IF EXISTS %Q", safe_index);
1251 ret = camel_db_add_to_transaction (cdb, table_creation_query, error);
1252 g_free (safe_index);
1253 sqlite3_free (table_creation_query);
1255 /* INDEX on preview */
1256 safe_index = g_strdup_printf ("SINDEX-%s-preview", folder_name);
1257 table_creation_query = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS %Q ON '%q_preview' (uid, preview)", safe_index, folder_name);
1258 ret = camel_db_add_to_transaction (cdb, table_creation_query, error);
1259 g_free (safe_index);
1260 sqlite3_free (table_creation_query);
1262 /* Index on deleted*/
1263 safe_index = g_strdup_printf ("DELINDEX-%s", folder_name);
1264 table_creation_query = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS %Q ON %Q (deleted)", safe_index, folder_name);
1265 ret = camel_db_add_to_transaction (cdb, table_creation_query, error);
1266 g_free (safe_index);
1267 sqlite3_free (table_creation_query);
1270 safe_index = g_strdup_printf ("JUNKINDEX-%s", folder_name);
1271 table_creation_query = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS %Q ON %Q (junk)", safe_index, folder_name);
1272 ret = camel_db_add_to_transaction (cdb, table_creation_query, error);
1273 g_free (safe_index);
1274 sqlite3_free (table_creation_query);
1276 /* Index on unread*/
1277 safe_index = g_strdup_printf ("READINDEX-%s", folder_name);
1278 table_creation_query = sqlite3_mprintf ("CREATE INDEX IF NOT EXISTS %Q ON %Q (read)", safe_index, folder_name);
1279 ret = camel_db_add_to_transaction (cdb, table_creation_query, error);
1280 g_free (safe_index);
1281 sqlite3_free (table_creation_query);
1287 camel_db_migrate_folder_prepare (CamelDB *cdb,
1288 const gchar *folder_name,
1293 gchar *table_creation_query;
1295 /* Migration stage one: storing the old data */
1299 /* Between version 0-1 the following things are changed
1300 * ADDED: created: time
1301 * ADDED: modified: time
1302 * RENAMED: msg_security to dirty
1305 table_creation_query = sqlite3_mprintf ("DROP TABLE IF EXISTS 'mem.%q'", folder_name);
1306 ret = camel_db_add_to_transaction (cdb, table_creation_query, error);
1307 sqlite3_free (table_creation_query);
1309 table_creation_query = sqlite3_mprintf ("CREATE TEMP TABLE IF NOT EXISTS 'mem.%q' ( uid TEXT PRIMARY KEY , flags INTEGER , msg_type INTEGER , read INTEGER , deleted INTEGER , replied INTEGER , important INTEGER , junk INTEGER , attachment INTEGER , dirty INTEGER , size INTEGER , dsent NUMERIC , dreceived NUMERIC , subject TEXT , mail_from TEXT , mail_to TEXT , mail_cc TEXT , mlist TEXT , followup_flag TEXT , followup_completed_on TEXT , followup_due_by TEXT , part TEXT , labels TEXT , usertags TEXT , cinfo TEXT , bdata TEXT, created TEXT, modified TEXT )", folder_name);
1310 ret = camel_db_add_to_transaction (cdb, table_creation_query, error);
1311 sqlite3_free (table_creation_query);
1313 table_creation_query = sqlite3_mprintf ("INSERT INTO 'mem.%q' SELECT uid , flags , msg_type , read , deleted , replied , important , junk , attachment , dirty , size , dsent , dreceived , subject , mail_from , mail_to , mail_cc , mlist , followup_flag , followup_completed_on , followup_due_by , part , labels , usertags , cinfo , bdata , strftime(\"%%s\", 'now'), strftime(\"%%s\", 'now') FROM %Q", folder_name, folder_name);
1314 ret = camel_db_add_to_transaction (cdb, table_creation_query, error);
1315 sqlite3_free (table_creation_query);
1317 table_creation_query = sqlite3_mprintf ("DROP TABLE IF EXISTS %Q", folder_name);
1318 ret = camel_db_add_to_transaction (cdb, table_creation_query, error);
1319 sqlite3_free (table_creation_query);
1321 ret = camel_db_create_message_info_table (cdb, folder_name, error);
1322 g_clear_error (error);
1325 /* Add later version migrations here */
1331 camel_db_migrate_folder_recreate (CamelDB *cdb,
1332 const gchar *folder_name,
1337 gchar *table_creation_query;
1339 /* Migration stage two: writing back the old data */
1342 GError *local_error = NULL;
1344 table_creation_query = sqlite3_mprintf ("INSERT INTO %Q SELECT uid , flags , msg_type , read , deleted , replied , important , junk , attachment , dirty , size , dsent , dreceived , subject , mail_from , mail_to , mail_cc , mlist , followup_flag , followup_completed_on , followup_due_by , part , labels , usertags , cinfo , bdata, created, modified FROM 'mem.%q'", folder_name, folder_name);
1345 ret = camel_db_add_to_transaction (cdb, table_creation_query, &local_error);
1346 sqlite3_free (table_creation_query);
1349 table_creation_query = sqlite3_mprintf ("DROP TABLE 'mem.%q'", folder_name);
1350 ret = camel_db_add_to_transaction (cdb, table_creation_query, &local_error);
1351 sqlite3_free (table_creation_query);
1355 if (local_error->message && strstr (local_error->message, "no such table") != NULL) {
1356 /* ignore 'no such table' errors here */
1357 g_clear_error (&local_error);
1360 g_propagate_error (error, local_error);
1365 /* Add later version migrations here */
1371 * camel_db_reset_folder_version:
1376 camel_db_reset_folder_version (CamelDB *cdb,
1377 const gchar *folder_name,
1382 gchar *version_creation_query;
1383 gchar *version_insert_query;
1384 gchar *drop_folder_query;
1386 drop_folder_query = sqlite3_mprintf ("DROP TABLE IF EXISTS '%q_version'", folder_name);
1387 version_creation_query = sqlite3_mprintf ("CREATE TABLE IF NOT EXISTS '%q_version' ( version TEXT )", folder_name);
1389 version_insert_query = sqlite3_mprintf ("INSERT INTO '%q_version' VALUES ('%d')", folder_name, reset_version);
1391 ret = camel_db_add_to_transaction (cdb, drop_folder_query, error);
1392 ret = camel_db_add_to_transaction (cdb, version_creation_query, error);
1393 ret = camel_db_add_to_transaction (cdb, version_insert_query, error);
1395 sqlite3_free (drop_folder_query);
1396 sqlite3_free (version_creation_query);
1397 sqlite3_free (version_insert_query);
1403 camel_db_write_folder_version (CamelDB *cdb,
1404 const gchar *folder_name,
1409 gchar *version_creation_query;
1410 gchar *version_insert_query;
1412 version_creation_query = sqlite3_mprintf ("CREATE TABLE IF NOT EXISTS '%q_version' ( version TEXT )", folder_name);
1414 if (old_version == -1)
1415 version_insert_query = sqlite3_mprintf ("INSERT INTO '%q_version' VALUES ('2')", folder_name);
1417 version_insert_query = sqlite3_mprintf ("UPDATE '%q_version' SET version='2'", folder_name);
1419 ret = camel_db_add_to_transaction (cdb, version_creation_query, error);
1420 ret = camel_db_add_to_transaction (cdb, version_insert_query, error);
1422 sqlite3_free (version_creation_query);
1423 sqlite3_free (version_insert_query);
1429 read_version_callback (gpointer ref,
1434 gint *version = (gint *) ref;
1437 *version = strtoul (cols [0], NULL, 10);
1443 camel_db_get_folder_version (CamelDB *cdb,
1444 const gchar *folder_name,
1450 query = sqlite3_mprintf ("SELECT version FROM '%q_version'", folder_name);
1451 camel_db_select (cdb, query, read_version_callback, &version, error);
1452 sqlite3_free (query);
1458 * camel_db_prepare_message_info_table:
1463 camel_db_prepare_message_info_table (CamelDB *cdb,
1464 const gchar *folder_name,
1467 gint ret, current_version;
1470 /* Make sure we have the table already */
1471 camel_db_begin_transaction (cdb, &err);
1472 ret = camel_db_create_message_info_table (cdb, folder_name, &err);
1476 camel_db_end_transaction (cdb, &err);
1478 /* Migration stage zero: version fetch */
1479 current_version = camel_db_get_folder_version (cdb, folder_name, &err);
1481 camel_db_begin_transaction (cdb, &err);
1483 /* Migration stage one: storing the old data if necessary */
1484 ret = camel_db_migrate_folder_prepare (cdb, folder_name, current_version, &err);
1488 /* Migration stage two: rewriting the old data if necessary */
1489 ret = camel_db_migrate_folder_recreate (cdb, folder_name, current_version, &err);
1493 /* Final step: (over)write the current version label */
1494 ret = camel_db_write_folder_version (cdb, folder_name, current_version, &err);
1498 camel_db_end_transaction (cdb, &err);
1501 if (err && cdb->priv->transaction_is_on)
1502 camel_db_abort_transaction (cdb, NULL);
1505 g_propagate_error (error, err);
1511 write_mir (CamelDB *cdb,
1512 const gchar *folder_name,
1513 CamelMIRecord *record,
1515 gboolean delete_old_record)
1518 /*char *del_query;*/
1521 /* FIXME: We should migrate from this DELETE followed by INSERT model to an INSERT OR REPLACE model as pointed out by pvanhoof */
1523 /* NB: UGLIEST Hack. We can't modify the schema now. We are using dirty (an unsed one to notify of FLAGGED/Dirty infos */
1525 ins_query = sqlite3_mprintf (
1526 "INSERT OR REPLACE INTO %Q VALUES ("
1527 "%Q, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, "
1528 "%lld, %lld, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %Q, "
1529 "%Q, %Q, %Q, %Q, %Q, "
1530 "strftime(\"%%s\", 'now'), "
1531 "strftime(\"%%s\", 'now') )",
1544 (gint64) record->dsent,
1545 (gint64) record->dreceived,
1551 record->followup_flag,
1552 record->followup_completed_on,
1553 record->followup_due_by,
1560 ret = camel_db_add_to_transaction (cdb, ins_query, error);
1562 sqlite3_free (ins_query);
1565 ins_query = sqlite3_mprintf (
1566 "INSERT OR REPLACE INTO "
1567 "'%q_bodystructure' VALUES (%Q, %Q )",
1568 folder_name, record->uid, record->bodystructure);
1569 ret = camel_db_add_to_transaction (cdb, ins_query, error);
1570 sqlite3_free (ins_query);
1577 * camel_db_write_fresh_message_info_record:
1582 camel_db_write_fresh_message_info_record (CamelDB *cdb,
1583 const gchar *folder_name,
1584 CamelMIRecord *record,
1587 return write_mir (cdb, folder_name, record, error, FALSE);
1591 * camel_db_write_message_info_record:
1596 camel_db_write_message_info_record (CamelDB *cdb,
1597 const gchar *folder_name,
1598 CamelMIRecord *record,
1601 return write_mir (cdb, folder_name, record, error, TRUE);
1605 * camel_db_write_folder_info_record:
1610 camel_db_write_folder_info_record (CamelDB *cdb,
1611 CamelFIRecord *record,
1619 ins_query = sqlite3_mprintf (
1620 "INSERT INTO folders VALUES ("
1621 "%Q, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %Q ) ",
1622 record->folder_name,
1627 record->saved_count,
1628 record->unread_count,
1629 record->deleted_count,
1631 record->visible_count,
1635 del_query = sqlite3_mprintf (
1636 "DELETE FROM folders WHERE folder_name = %Q",
1637 record->folder_name);
1639 ret = camel_db_add_to_transaction (cdb, del_query, error);
1640 ret = camel_db_add_to_transaction (cdb, ins_query, error);
1642 sqlite3_free (del_query);
1643 sqlite3_free (ins_query);
1648 struct ReadFirData {
1649 GHashTable *columns_hash;
1650 CamelFIRecord *record;
1654 read_fir_callback (gpointer ref,
1659 struct ReadFirData *rfd = ref;
1662 d (g_print ("\nread_fir_callback called \n"));
1664 for (i = 0; i < ncol; ++i) {
1665 if (!name[i] || !cols[i])
1668 switch (camel_db_get_column_ident (&rfd->columns_hash, i, ncol, name)) {
1669 case CAMEL_DB_COLUMN_FOLDER_NAME:
1670 rfd->record->folder_name = g_strdup (cols[i]);
1672 case CAMEL_DB_COLUMN_VERSION:
1673 rfd->record->version = cols[i] ? strtoul (cols[i], NULL, 10) : 0;
1675 case CAMEL_DB_COLUMN_FLAGS:
1676 rfd->record->flags = cols[i] ? strtoul (cols[i], NULL, 10) : 0;
1678 case CAMEL_DB_COLUMN_NEXTUID:
1679 rfd->record->nextuid = cols[i] ? strtoul (cols[i], NULL, 10) : 0;
1681 case CAMEL_DB_COLUMN_TIME:
1682 rfd->record->time = cols[i] ? strtoul (cols[i], NULL, 10) : 0;
1684 case CAMEL_DB_COLUMN_SAVED_COUNT:
1685 rfd->record->saved_count = cols[i] ? strtoul (cols[i], NULL, 10) : 0;
1687 case CAMEL_DB_COLUMN_UNREAD_COUNT:
1688 rfd->record->unread_count = cols[i] ? strtoul (cols[i], NULL, 10) : 0;
1690 case CAMEL_DB_COLUMN_DELETED_COUNT:
1691 rfd->record->deleted_count = cols[i] ? strtoul (cols[i], NULL, 10) : 0;
1693 case CAMEL_DB_COLUMN_JUNK_COUNT:
1694 rfd->record->junk_count = cols[i] ? strtoul (cols[i], NULL, 10) : 0;
1696 case CAMEL_DB_COLUMN_VISIBLE_COUNT:
1697 rfd->record->visible_count = cols[i] ? strtoul (cols[i], NULL, 10) : 0;
1699 case CAMEL_DB_COLUMN_JND_COUNT:
1700 rfd->record->jnd_count = cols[i] ? strtoul (cols[i], NULL, 10) : 0;
1702 case CAMEL_DB_COLUMN_BDATA:
1703 rfd->record->bdata = g_strdup (cols[i]);
1706 g_warn_if_reached ();
1715 * camel_db_read_folder_info_record:
1720 camel_db_read_folder_info_record (CamelDB *cdb,
1721 const gchar *folder_name,
1722 CamelFIRecord *record,
1725 struct ReadFirData rfd;
1729 rfd.columns_hash = NULL;
1730 rfd.record = record;
1732 query = sqlite3_mprintf ("SELECT * FROM folders WHERE folder_name = %Q", folder_name);
1733 ret = camel_db_select (cdb, query, read_fir_callback, &rfd, error);
1734 sqlite3_free (query);
1736 if (rfd.columns_hash)
1737 g_hash_table_destroy (rfd.columns_hash);
1743 * camel_db_read_message_info_record_with_uid:
1748 camel_db_read_message_info_record_with_uid (CamelDB *cdb,
1749 const gchar *folder_name,
1752 CamelDBSelectCB read_mir_callback,
1758 query = sqlite3_mprintf ("SELECT uid, flags, size, dsent, dreceived, subject, mail_from, mail_to, mail_cc, mlist, part, labels, usertags, cinfo, bdata FROM %Q WHERE uid = %Q", folder_name, uid);
1759 ret = camel_db_select (cdb, query, read_mir_callback, p, error);
1760 sqlite3_free (query);
1766 * camel_db_read_message_info_records:
1771 camel_db_read_message_info_records (CamelDB *cdb,
1772 const gchar *folder_name,
1774 CamelDBSelectCB read_mir_callback,
1780 query = sqlite3_mprintf ("SELECT uid, flags, size, dsent, dreceived, subject, mail_from, mail_to, mail_cc, mlist, part, labels, usertags, cinfo, bdata FROM %Q ", folder_name);
1781 ret = camel_db_select (cdb, query, read_mir_callback, p, error);
1782 sqlite3_free (query);
1788 * camel_db_create_deleted_table:
1793 camel_db_create_deleted_table (CamelDB *cdb,
1797 gchar *table_creation_query;
1798 table_creation_query = sqlite3_mprintf ("CREATE TABLE IF NOT EXISTS Deletes (id INTEGER primary key AUTOINCREMENT not null, uid TEXT, time TEXT, mailbox TEXT)");
1799 ret = camel_db_add_to_transaction (cdb, table_creation_query, error);
1800 sqlite3_free (table_creation_query);
1805 camel_db_trim_deleted_table (CamelDB *cdb,
1810 /* TODO: We need a mechanism to get rid of very old deletes, or something
1811 * that keeps the list trimmed at a certain max (deleting upfront when
1812 * appending at the back) */
1818 * camel_db_delete_uid:
1823 camel_db_delete_uid (CamelDB *cdb,
1824 const gchar *folder,
1831 camel_db_begin_transaction (cdb, error);
1833 ret = camel_db_create_deleted_table (cdb, error);
1835 tab = sqlite3_mprintf ("INSERT OR REPLACE INTO Deletes (uid, mailbox, time) SELECT uid, %Q, strftime(\"%%s\", 'now') FROM %Q WHERE uid = %Q", folder, folder, uid);
1836 ret = camel_db_add_to_transaction (cdb, tab, error);
1839 ret = camel_db_trim_deleted_table (cdb, error);
1841 tab = sqlite3_mprintf ("DELETE FROM '%q_bodystructure' WHERE uid = %Q", folder, uid);
1842 ret = camel_db_add_to_transaction (cdb, tab, error);
1845 tab = sqlite3_mprintf ("DELETE FROM %Q WHERE uid = %Q", folder, uid);
1846 ret = camel_db_add_to_transaction (cdb, tab, error);
1849 ret = camel_db_end_transaction (cdb, error);
1851 CAMEL_DB_RELEASE_SQLITE_MEMORY;
1856 cdb_delete_ids (CamelDB *cdb,
1857 const gchar *folder_name,
1859 const gchar *uid_prefix,
1866 gboolean first = TRUE;
1867 GString *str = g_string_new ("DELETE FROM ");
1869 GString *ins_str = NULL;
1871 if (strcmp (field, "vuid") != 0)
1872 ins_str = g_string_new ("INSERT OR REPLACE INTO Deletes (uid, mailbox, time) SELECT uid, ");
1874 camel_db_begin_transaction (cdb, error);
1877 ret = camel_db_create_deleted_table (cdb, error);
1880 tab = sqlite3_mprintf ("%Q, strftime(\"%%s\", 'now') FROM %Q WHERE %s IN (", folder_name, folder_name, field);
1881 g_string_append_printf (ins_str, "%s ", tab);
1885 tmp = sqlite3_mprintf ("%Q WHERE %s IN (", folder_name, field);
1886 g_string_append_printf (str, "%s ", tmp);
1892 gchar *foo = g_strdup_printf ("%s%s", uid_prefix, (gchar *) iterator->data);
1893 tmp = sqlite3_mprintf ("%Q", foo);
1895 iterator = iterator->next;
1897 if (first == TRUE) {
1898 g_string_append_printf (str, " %s ", tmp);
1900 g_string_append_printf (ins_str, " %s ", tmp);
1903 g_string_append_printf (str, ", %s ", tmp);
1905 g_string_append_printf (ins_str, ", %s ", tmp);
1911 g_string_append (str, ")");
1913 g_string_append (ins_str, ")");
1914 ret = camel_db_add_to_transaction (cdb, ins_str->str, error);
1915 ret = camel_db_trim_deleted_table (cdb, error);
1918 ret = camel_db_add_to_transaction (cdb, str->str, error);
1920 ret = camel_db_end_transaction (cdb, error);
1922 CAMEL_DB_RELEASE_SQLITE_MEMORY;
1925 g_string_free (ins_str, TRUE);
1926 g_string_free (str, TRUE);
1932 * camel_db_delete_uids:
1937 camel_db_delete_uids (CamelDB *cdb,
1938 const gchar *folder_name,
1942 if (!uids || !uids->data)
1945 return cdb_delete_ids (cdb, folder_name, uids, "", "uid", error);
1949 * camel_db_clear_folder_summary:
1954 camel_db_clear_folder_summary (CamelDB *cdb,
1955 const gchar *folder,
1965 folders_del = sqlite3_mprintf ("DELETE FROM folders WHERE folder_name = %Q", folder);
1966 msginfo_del = sqlite3_mprintf ("DELETE FROM %Q ", folder);
1967 bstruct_del = sqlite3_mprintf ("DELETE FROM '%q_bodystructure' ", folder);
1969 camel_db_begin_transaction (cdb, error);
1971 ret = camel_db_create_deleted_table (cdb, error);
1973 tab = sqlite3_mprintf ("INSERT OR REPLACE INTO Deletes (uid, mailbox, time) SELECT uid, %Q, strftime(\"%%s\", 'now') FROM %Q", folder, folder);
1974 ret = camel_db_add_to_transaction (cdb, tab, error);
1977 ret = camel_db_trim_deleted_table (cdb, error);
1979 camel_db_add_to_transaction (cdb, msginfo_del, error);
1980 camel_db_add_to_transaction (cdb, folders_del, error);
1981 camel_db_add_to_transaction (cdb, bstruct_del, error);
1983 ret = camel_db_end_transaction (cdb, error);
1985 sqlite3_free (folders_del);
1986 sqlite3_free (msginfo_del);
1987 sqlite3_free (bstruct_del);
1993 * camel_db_delete_folder:
1998 camel_db_delete_folder (CamelDB *cdb,
1999 const gchar *folder,
2006 camel_db_begin_transaction (cdb, error);
2008 ret = camel_db_create_deleted_table (cdb, error);
2010 tab = sqlite3_mprintf ("INSERT OR REPLACE INTO Deletes (uid, mailbox, time) SELECT uid, %Q, strftime(\"%%s\", 'now') FROM %Q", folder, folder);
2011 ret = camel_db_add_to_transaction (cdb, tab, error);
2014 ret = camel_db_trim_deleted_table (cdb, error);
2016 del = sqlite3_mprintf ("DELETE FROM folders WHERE folder_name = %Q", folder);
2017 ret = camel_db_add_to_transaction (cdb, del, error);
2020 del = sqlite3_mprintf ("DROP TABLE %Q ", folder);
2021 ret = camel_db_add_to_transaction (cdb, del, error);
2024 del = sqlite3_mprintf ("DROP TABLE '%q_bodystructure' ", folder);
2025 ret = camel_db_add_to_transaction (cdb, del, error);
2028 ret = camel_db_end_transaction (cdb, error);
2030 CAMEL_DB_RELEASE_SQLITE_MEMORY;
2035 * camel_db_rename_folder:
2040 camel_db_rename_folder (CamelDB *cdb,
2041 const gchar *old_folder,
2042 const gchar *new_folder,
2048 camel_db_begin_transaction (cdb, error);
2050 ret = camel_db_create_deleted_table (cdb, error);
2052 tab = sqlite3_mprintf ("INSERT OR REPLACE INTO Deletes (uid, mailbox, time) SELECT uid, %Q, strftime(\"%%s\", 'now') FROM %Q", old_folder, old_folder);
2053 ret = camel_db_add_to_transaction (cdb, tab, error);
2056 ret = camel_db_trim_deleted_table (cdb, error);
2058 cmd = sqlite3_mprintf ("ALTER TABLE %Q RENAME TO %Q", old_folder, new_folder);
2059 ret = camel_db_add_to_transaction (cdb, cmd, error);
2062 cmd = sqlite3_mprintf ("ALTER TABLE '%q_version' RENAME TO '%q_version'", old_folder, new_folder);
2063 ret = camel_db_add_to_transaction (cdb, cmd, error);
2066 cmd = sqlite3_mprintf ("UPDATE %Q SET modified=strftime(\"%%s\", 'now'), created=strftime(\"%%s\", 'now')", new_folder);
2067 ret = camel_db_add_to_transaction (cdb, cmd, error);
2070 cmd = sqlite3_mprintf ("UPDATE folders SET folder_name = %Q WHERE folder_name = %Q", new_folder, old_folder);
2071 ret = camel_db_add_to_transaction (cdb, cmd, error);
2074 ret = camel_db_end_transaction (cdb, error);
2076 CAMEL_DB_RELEASE_SQLITE_MEMORY;
2081 * camel_db_camel_mir_free:
2086 camel_db_camel_mir_free (CamelMIRecord *record)
2089 camel_pstring_free (record->uid);
2090 camel_pstring_free (record->subject);
2091 camel_pstring_free (record->from);
2092 camel_pstring_free (record->to);
2093 camel_pstring_free (record->cc);
2094 camel_pstring_free (record->mlist);
2095 camel_pstring_free (record->followup_flag);
2096 camel_pstring_free (record->followup_completed_on);
2097 camel_pstring_free (record->followup_due_by);
2098 g_free (record->part);
2099 g_free (record->labels);
2100 g_free (record->usertags);
2101 g_free (record->cinfo);
2102 g_free (record->bdata);
2103 g_free (record->bodystructure);
2110 * camel_db_sqlize_string:
2115 camel_db_sqlize_string (const gchar *string)
2117 return sqlite3_mprintf ("%Q", string);
2121 * camel_db_free_sqlized_string:
2126 camel_db_free_sqlized_string (gchar *string)
2128 sqlite3_free (string);
2133 "( uid TEXT PRIMARY KEY ,
2142 followup_flag TEXT ,
2143 followup_completed_on TEXT ,
2144 followup_due_by TEXT ," */
2147 * camel_db_get_column_name:
2152 camel_db_get_column_name (const gchar *raw_name)
2154 if (!g_ascii_strcasecmp (raw_name, "Subject"))
2155 return g_strdup ("subject");
2156 else if (!g_ascii_strcasecmp (raw_name, "from"))
2157 return g_strdup ("mail_from");
2158 else if (!g_ascii_strcasecmp (raw_name, "Cc"))
2159 return g_strdup ("mail_cc");
2160 else if (!g_ascii_strcasecmp (raw_name, "To"))
2161 return g_strdup ("mail_to");
2162 else if (!g_ascii_strcasecmp (raw_name, "Flagged"))
2163 return g_strdup ("important");
2164 else if (!g_ascii_strcasecmp (raw_name, "deleted"))
2165 return g_strdup ("deleted");
2166 else if (!g_ascii_strcasecmp (raw_name, "junk"))
2167 return g_strdup ("junk");
2168 else if (!g_ascii_strcasecmp (raw_name, "Answered"))
2169 return g_strdup ("replied");
2170 else if (!g_ascii_strcasecmp (raw_name, "Seen"))
2171 return g_strdup ("read");
2172 else if (!g_ascii_strcasecmp (raw_name, "user-tag"))
2173 return g_strdup ("usertags");
2174 else if (!g_ascii_strcasecmp (raw_name, "user-flag"))
2175 return g_strdup ("labels");
2176 else if (!g_ascii_strcasecmp (raw_name, "Attachments"))
2177 return g_strdup ("attachment");
2178 else if (!g_ascii_strcasecmp (raw_name, "x-camel-mlist"))
2179 return g_strdup ("mlist");
2181 return g_strdup (raw_name);
2186 * camel_db_start_in_memory_transactions:
2191 camel_db_start_in_memory_transactions (CamelDB *cdb,
2195 gchar *cmd = sqlite3_mprintf ("ATTACH DATABASE ':memory:' AS %s", CAMEL_DB_IN_MEMORY_DB);
2197 ret = camel_db_command (cdb, cmd, error);
2200 cmd = sqlite3_mprintf ("CREATE TEMPORARY TABLE %Q ( uid TEXT PRIMARY KEY , flags INTEGER , msg_type INTEGER , read INTEGER , deleted INTEGER , replied INTEGER , important INTEGER , junk INTEGER , attachment INTEGER , dirty INTEGER , size INTEGER , dsent NUMERIC , dreceived NUMERIC , subject TEXT , mail_from TEXT , mail_to TEXT , mail_cc TEXT , mlist TEXT , followup_flag TEXT , followup_completed_on TEXT , followup_due_by TEXT , part TEXT , labels TEXT , usertags TEXT , cinfo TEXT , bdata TEXT )", CAMEL_DB_IN_MEMORY_TABLE);
2201 ret = camel_db_command (cdb, cmd, error);
2210 * camel_db_flush_in_memory_transactions:
2215 camel_db_flush_in_memory_transactions (CamelDB *cdb,
2216 const gchar *folder_name,
2220 gchar *cmd = sqlite3_mprintf ("INSERT INTO %Q SELECT * FROM %Q", folder_name, CAMEL_DB_IN_MEMORY_TABLE);
2222 ret = camel_db_command (cdb, cmd, error);
2225 cmd = sqlite3_mprintf ("DROP TABLE %Q", CAMEL_DB_IN_MEMORY_TABLE);
2226 ret = camel_db_command (cdb, cmd, error);
2229 cmd = sqlite3_mprintf ("DETACH %Q", CAMEL_DB_IN_MEMORY_DB);
2230 ret = camel_db_command (cdb, cmd, error);
2236 static struct _known_column_names {
2238 CamelDBKnownColumnNames ident;
2239 } known_column_names[] = {
2240 { "attachment", CAMEL_DB_COLUMN_ATTACHMENT },
2241 { "bdata", CAMEL_DB_COLUMN_BDATA },
2242 { "bodystructure", CAMEL_DB_COLUMN_BODYSTRUCTURE },
2243 { "cinfo", CAMEL_DB_COLUMN_CINFO },
2244 { "deleted", CAMEL_DB_COLUMN_DELETED },
2245 { "deleted_count", CAMEL_DB_COLUMN_DELETED_COUNT },
2246 { "dreceived", CAMEL_DB_COLUMN_DRECEIVED },
2247 { "dsent", CAMEL_DB_COLUMN_DSENT },
2248 { "flags", CAMEL_DB_COLUMN_FLAGS },
2249 { "folder_name", CAMEL_DB_COLUMN_FOLDER_NAME },
2250 { "followup_completed_on", CAMEL_DB_COLUMN_FOLLOWUP_COMPLETED_ON },
2251 { "followup_due_by", CAMEL_DB_COLUMN_FOLLOWUP_DUE_BY },
2252 { "followup_flag", CAMEL_DB_COLUMN_FOLLOWUP_FLAG },
2253 { "important", CAMEL_DB_COLUMN_IMPORTANT },
2254 { "jnd_count", CAMEL_DB_COLUMN_JND_COUNT },
2255 { "junk", CAMEL_DB_COLUMN_JUNK },
2256 { "junk_count", CAMEL_DB_COLUMN_JUNK_COUNT },
2257 { "labels", CAMEL_DB_COLUMN_LABELS },
2258 { "mail_cc", CAMEL_DB_COLUMN_MAIL_CC },
2259 { "mail_from", CAMEL_DB_COLUMN_MAIL_FROM },
2260 { "mail_to", CAMEL_DB_COLUMN_MAIL_TO },
2261 { "mlist", CAMEL_DB_COLUMN_MLIST },
2262 { "nextuid", CAMEL_DB_COLUMN_NEXTUID },
2263 { "part", CAMEL_DB_COLUMN_PART },
2264 { "preview", CAMEL_DB_COLUMN_PREVIEW },
2265 { "read", CAMEL_DB_COLUMN_READ },
2266 { "replied", CAMEL_DB_COLUMN_REPLIED },
2267 { "saved_count", CAMEL_DB_COLUMN_SAVED_COUNT },
2268 { "size", CAMEL_DB_COLUMN_SIZE },
2269 { "subject", CAMEL_DB_COLUMN_SUBJECT },
2270 { "time", CAMEL_DB_COLUMN_TIME },
2271 { "uid", CAMEL_DB_COLUMN_UID },
2272 { "unread_count", CAMEL_DB_COLUMN_UNREAD_COUNT },
2273 { "usertags", CAMEL_DB_COLUMN_USERTAGS },
2274 { "version", CAMEL_DB_COLUMN_VERSION },
2275 { "visible_count", CAMEL_DB_COLUMN_VISIBLE_COUNT },
2276 { "vuid", CAMEL_DB_COLUMN_VUID }
2280 * camel_db_get_column_ident:
2282 * Traverses column name from index @index into an enum
2283 * #CamelDBKnownColumnNames value. The @col_names contains @ncols columns.
2284 * First time this is called is created the @hash from col_names indexes into
2285 * the enum, and this is reused for every other call. The function expects
2286 * that column names are returned always in the same order. When all rows
2287 * are read the @hash table can be freed with g_hash_table_destroy().
2291 CamelDBKnownColumnNames
2292 camel_db_get_column_ident (GHashTable **hash,
2297 gpointer value = NULL;
2299 g_return_val_if_fail (hash != NULL, CAMEL_DB_COLUMN_UNKNOWN);
2300 g_return_val_if_fail (col_names != NULL, CAMEL_DB_COLUMN_UNKNOWN);
2301 g_return_val_if_fail (ncols > 0, CAMEL_DB_COLUMN_UNKNOWN);
2302 g_return_val_if_fail (index >= 0, CAMEL_DB_COLUMN_UNKNOWN);
2303 g_return_val_if_fail (index < ncols, CAMEL_DB_COLUMN_UNKNOWN);
2306 gint ii, jj, from, max = G_N_ELEMENTS (known_column_names);
2308 *hash = g_hash_table_new (g_direct_hash, g_direct_equal);
2310 for (ii = 0, jj = 0; ii < ncols; ii++) {
2311 const gchar *name = col_names[ii];
2312 gboolean first = TRUE;
2317 for (from = jj; first || jj != from; jj = (jj + 1) % max, first = FALSE) {
2318 if (g_str_equal (name, known_column_names[jj].name)) {
2319 g_hash_table_insert (*hash, GINT_TO_POINTER (ii), GINT_TO_POINTER (known_column_names[jj].ident));
2324 if (from == jj && !first)
2325 g_warning ("%s: missing column name '%s' in a list of known columns", G_STRFUNC, name);
2329 g_return_val_if_fail (g_hash_table_lookup_extended (*hash, GINT_TO_POINTER (index), NULL, &value), CAMEL_DB_COLUMN_UNKNOWN);
2331 return GPOINTER_TO_INT (value);