Index: camel/camel-store.c =================================================================== --- camel/camel-store.c (revision 9841) +++ camel/camel-store.c (revision 9842) @@ -322,6 +322,21 @@ } if (!folder) { + + if (flags & CAMEL_STORE_IS_MIGRATING) { + if ((store->flags & CAMEL_STORE_VTRASH) && strcmp(folder_name, CAMEL_VTRASH_NAME) == 0) { + if (store->folders) + camel_object_bag_abort(store->folders, folder_name); + return NULL; + } + + if ((store->flags & CAMEL_STORE_VJUNK) && strcmp(folder_name, CAMEL_VJUNK_NAME) == 0) { + if (store->folders) + camel_object_bag_abort(store->folders, folder_name); + return NULL; + } + } + if ((store->flags & CAMEL_STORE_VTRASH) && strcmp(folder_name, CAMEL_VTRASH_NAME) == 0) { folder = CS_CLASS(store)->get_trash(store, ex); } else if ((store->flags & CAMEL_STORE_VJUNK) && strcmp(folder_name, CAMEL_VJUNK_NAME) == 0) { Index: camel/camel-store.h =================================================================== --- camel/camel-store.h (revision 9841) +++ camel/camel-store.h (revision 9842) @@ -115,6 +115,7 @@ #define CAMEL_STORE_FILTER_INBOX (1 << 2) #define CAMEL_STORE_VJUNK (1 << 3) #define CAMEL_STORE_PROXY (1 << 4) +#define CAMEL_STORE_IS_MIGRATING (1 << 5) struct _CamelDB; Index: camel/providers/local/camel-local-folder.c =================================================================== --- camel/providers/local/camel-local-folder.c (revision 9841) +++ camel/providers/local/camel-local-folder.c (revision 9842) @@ -289,7 +289,7 @@ } folder->summary = (CamelFolderSummary *)CLOCALF_CLASS(lf)->create_summary(lf, lf->summary_path, lf->folder_path, lf->index); - if (camel_local_summary_load((CamelLocalSummary *)folder->summary, forceindex, NULL) == -1) { + if (!(flags & CAMEL_STORE_IS_MIGRATING) && camel_local_summary_load((CamelLocalSummary *)folder->summary, forceindex, NULL) == -1) { /* ? */ if (camel_local_summary_check((CamelLocalSummary *)folder->summary, lf->changes, ex) == 0) { /* we sync here so that any hard work setting up the folder isn't lost */ Index: camel/camel-db.c =================================================================== --- camel/camel-db.c (revision 9841) +++ camel/camel-db.c (revision 9842) @@ -47,6 +47,8 @@ static GStaticRecMutex trans_lock = G_STATIC_REC_MUTEX_INIT; +static int write_mir (CamelDB *cdb, const char *folder_name, CamelMIRecord *record, CamelException *ex, gboolean delete_old_record); + static int cdb_sql_exec (sqlite3 *db, const char* stmt, CamelException *ex) { @@ -812,13 +814,29 @@ return ret; } + +int +camel_db_write_fresh_message_info_record (CamelDB *cdb, const char *folder_name, CamelMIRecord *record, CamelException *ex) +{ + return write_mir (cdb, folder_name, record, ex, FALSE); +} + + int camel_db_write_message_info_record (CamelDB *cdb, const char *folder_name, CamelMIRecord *record, CamelException *ex) { + return write_mir (cdb, folder_name, record, ex, TRUE); +} + +static int +write_mir (CamelDB *cdb, const char *folder_name, CamelMIRecord *record, CamelException *ex, gboolean delete_old_record) +{ int ret; char *del_query; char *ins_query; + /* FIXME: We should migrate from this DELETE followed by INSERT model to an INSERT OR REPLACE model as pointed out by pvanhoof */ + /* NB: UGLIEST Hack. We can't modify the schema now. We are using msg_security (an unsed one to notify of FLAGGED/Dirty infos */ ins_query = sqlite3_mprintf ("INSERT INTO %Q VALUES (%Q, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %ld, %ld, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %Q, %Q )", @@ -832,7 +850,8 @@ record->part, record->labels, record->usertags, record->cinfo, record->bdata); - del_query = sqlite3_mprintf ("DELETE FROM %Q WHERE uid = %Q", folder_name, record->uid); + if (delete_old_record) + del_query = sqlite3_mprintf ("DELETE FROM %Q WHERE uid = %Q", folder_name, record->uid); #if 0 char *upd_query; @@ -842,12 +861,14 @@ g_free (upd_query); #else - ret = camel_db_add_to_transaction (cdb, del_query, ex); + if (delete_old_record) + ret = camel_db_add_to_transaction (cdb, del_query, ex); ret = camel_db_add_to_transaction (cdb, ins_query, ex); #endif - sqlite3_free (del_query); + if (delete_old_record) + sqlite3_free (del_query); sqlite3_free (ins_query); return ret; @@ -1198,3 +1219,39 @@ CAMEL_DB_RELEASE_SQLITE_MEMORY; return ret; } + +int camel_db_start_in_memory_transactions (CamelDB *cdb, CamelException *ex) +{ + int ret; + char *cmd = sqlite3_mprintf ("ATTACH DATABASE ':memory:' AS %s", CAMEL_DB_IN_MEMORY_DB); + + ret = camel_db_command (cdb, cmd, ex); + sqlite3_free (cmd); + + 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 , msg_security 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); + ret = camel_db_command (cdb, cmd, ex); + if (ret != 0 ) + abort (); + sqlite3_free (cmd); + + return ret; +} + +int camel_db_flush_in_memory_transactions (CamelDB *cdb, const char * folder_name, CamelException *ex) +{ + int ret; + char *cmd = sqlite3_mprintf ("INSERT INTO %Q SELECT * FROM %Q", folder_name, CAMEL_DB_IN_MEMORY_TABLE); + + ret = camel_db_command (cdb, cmd, ex); + sqlite3_free (cmd); + + cmd = sqlite3_mprintf ("DROP TABLE %Q", CAMEL_DB_IN_MEMORY_TABLE); + ret = camel_db_command (cdb, cmd, ex); + sqlite3_free (cmd); + + cmd = sqlite3_mprintf ("DETACH %Q", CAMEL_DB_IN_MEMORY_DB); + ret = camel_db_command (cdb, cmd, ex); + sqlite3_free (cmd); + + return ret; +} Index: camel/camel-db.h =================================================================== --- camel/camel-db.h (revision 9841) +++ camel/camel-db.h (revision 9842) @@ -4,8 +4,14 @@ #define __CAMEL_DB_H #include #include + #define CAMEL_DB_FILE "folders.db" +/* Hopefully no one will create a folder named EVO_IN_meM_hAnDlE */ +#define CAMEL_DB_IN_MEMORY_TABLE "EVO_IN_meM_hAnDlE.temp" +#define CAMEL_DB_IN_MEMORY_DB "EVO_IN_meM_hAnDlE" +#define CAMEL_DB_IN_MEMORY_TABLE_LIMIT 100000 + #include "camel-exception.h" typedef struct _CamelDBPrivate CamelDBPrivate; @@ -132,6 +138,7 @@ int camel_db_prepare_message_info_table (CamelDB *cdb, const char *folder_name, CamelException *ex); int camel_db_write_message_info_record (CamelDB *cdb, const char *folder_name, CamelMIRecord *record, CamelException *ex); +int camel_db_write_fresh_message_info_record (CamelDB *cdb, const char *folder_name, CamelMIRecord *record, CamelException *ex); int camel_db_read_message_info_records (CamelDB *cdb, const char *folder_name, gpointer p, CamelDBSelectCB read_mir_callback, CamelException *ex); int camel_db_read_message_info_record_with_uid (CamelDB *cdb, const char *folder_name, const char *uid, gpointer p, CamelDBSelectCB read_mir_callback, CamelException *ex); @@ -168,5 +175,9 @@ int camel_db_set_collate (CamelDB *cdb, const char *col, const char *collate, CamelDBCollate func); /* Migration APIS */ int camel_db_migrate_vfolders_to_14(CamelDB *cdb, const char *folder, CamelException *ex); + +int camel_db_start_in_memory_transactions (CamelDB *cdb, CamelException *ex); +int camel_db_flush_in_memory_transactions (CamelDB *cdb, const char * folder_name, CamelException *ex); + #endif Index: camel/camel-folder-summary.c =================================================================== --- camel/camel-folder-summary.c (revision 9841) +++ camel/camel-folder-summary.c (revision 9842) @@ -129,7 +129,7 @@ static int content_info_save(CamelFolderSummary *, FILE *, CamelMessageContentInfo *); static void content_info_free(CamelFolderSummary *, CamelMessageContentInfo *); -static int save_message_infos_to_db (CamelFolderSummary *s, CamelException *ex); +static int save_message_infos_to_db (CamelFolderSummary *s, gboolean fresh_mir, CamelException *ex); static int camel_read_mir_callback (void * ref, int ncol, char ** cols, char ** name); static char *next_uid_string(CamelFolderSummary *s); @@ -1237,23 +1237,20 @@ if (fclose (in) != 0) return -1; + record = (((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_to_db (s, &ex)); + if (!record) { + return -1; + } - camel_db_begin_transaction (cdb, &ex); + ret = save_message_infos_to_db (s, TRUE, &ex); - ret = save_message_infos_to_db (s, &ex); - if (ret != 0) { - camel_db_abort_transaction (cdb, &ex); return -1; } - camel_db_end_transaction (cdb, &ex); - - record = (((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_to_db (s, &ex)); - if (!record) { - return -1; - } + camel_db_begin_transaction (cdb, &ex); ret = camel_db_write_folder_info_record (cdb, record, &ex); + camel_db_end_transaction (cdb, &ex); g_free (record->bdata); g_free (record); @@ -1321,17 +1318,24 @@ return 0; } +typedef struct { + CamelException *ex; + gboolean migration; + int progress; +} SaveToDBArgs; + static void save_to_db_cb (gpointer key, gpointer value, gpointer data) { - CamelException *ex = (CamelException *)data; + SaveToDBArgs *args = (SaveToDBArgs *) data; + CamelException *ex = args->ex; CamelMessageInfoBase *mi = (CamelMessageInfoBase *)value; CamelFolderSummary *s = (CamelFolderSummary *)mi->summary; char *folder_name = s->folder->full_name; CamelDB *cdb = s->folder->parent_store->cdb_w; CamelMIRecord *mir; - if (!mi->dirty) + if (!args->migration && !mi->dirty) return; mir = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_to_db (s, (CamelMessageInfo *)mi); @@ -1345,30 +1349,52 @@ } } - if (camel_db_write_message_info_record (cdb, folder_name, mir, ex) != 0) { - camel_db_camel_mir_free (mir); - return; + if (!args->migration) { + if (camel_db_write_message_info_record (cdb, folder_name, mir, ex) != 0) { + camel_db_camel_mir_free (mir); + return; + } + } else { + if (camel_db_write_fresh_message_info_record (cdb, CAMEL_DB_IN_MEMORY_TABLE, mir, ex) != 0) { + camel_db_camel_mir_free (mir); + return; + } + + if (args->progress > CAMEL_DB_IN_MEMORY_TABLE_LIMIT) { + g_print ("BULK INsert limit reached \n"); + camel_db_flush_in_memory_transactions (cdb, folder_name, ex); + camel_db_start_in_memory_transactions (cdb, ex); + args->progress = 0; + } else { + args->progress ++; + } } /* Reset the flags */ mi->dirty = FALSE; + mi->flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED; camel_db_camel_mir_free (mir); } static int -save_message_infos_to_db (CamelFolderSummary *s, CamelException *ex) +save_message_infos_to_db (CamelFolderSummary *s, gboolean fresh_mirs, CamelException *ex) { CamelDB *cdb = s->folder->parent_store->cdb_w; char *folder_name; + SaveToDBArgs args; + args.ex = ex; + args.migration = fresh_mirs; + args.progress = 0; + folder_name = s->folder->full_name; if (camel_db_prepare_message_info_table (cdb, folder_name, ex) != 0) { return -1; } CAMEL_SUMMARY_LOCK(s, summary_lock); /* Push MessageInfo-es */ - g_hash_table_foreach (s->loaded_infos, save_to_db_cb, ex); + g_hash_table_foreach (s->loaded_infos, save_to_db_cb, &args); CAMEL_SUMMARY_UNLOCK(s, summary_lock); /* FIXME[disk-summary] make sure we free the message infos that are loaded * are freed if not used anymore or should we leave that to the timer? */ @@ -1398,7 +1424,7 @@ camel_db_begin_transaction (cdb, ex); - ret = save_message_infos_to_db (s, ex); + ret = save_message_infos_to_db (s, FALSE, ex); if (ret != 0) { camel_db_abort_transaction (cdb, ex); /* Failed, so lets reset the flag */